Skip to content

Commit

Permalink
SwitchInt over Switch
Browse files Browse the repository at this point in the history
This removes another special case of Switch by replacing it with the more general SwitchInt. While
this is more clunky currently, there’s no reason we can’t make it nice (and efficient) to use.
  • Loading branch information
nagisa committed Feb 10, 2017
1 parent 5d70a7f commit aac82d9
Show file tree
Hide file tree
Showing 19 changed files with 105 additions and 130 deletions.
1 change: 1 addition & 0 deletions src/librustc/middle/const_val.rs
Expand Up @@ -14,6 +14,7 @@ use std::rc::Rc;
use hir::def_id::DefId;
use rustc_const_math::*;
use self::ConstVal::*;
pub use rustc_const_math::ConstInt;

use std::collections::BTreeMap;

Expand Down
17 changes: 1 addition & 16 deletions src/librustc/mir/mod.rs
Expand Up @@ -453,13 +453,6 @@ pub enum TerminatorKind<'tcx> {
target: BasicBlock,
},

/// lvalue evaluates to some enum; jump depending on the branch
Switch {
discr: Lvalue<'tcx>,
adt_def: &'tcx AdtDef,
targets: Vec<BasicBlock>,
},

/// operand evaluates to an integer; jump depending on its value
/// to one of the targets, and otherwise fallback to `otherwise`
SwitchInt {
Expand All @@ -471,6 +464,7 @@ pub enum TerminatorKind<'tcx> {

/// Possible values. The locations to branch to in each case
/// are found in the corresponding indices from the `targets` vector.
// FIXME: ConstVal doesn’t quite make any sense here? Its a Switch*Int*.
values: Vec<ConstVal>,

/// Possible branch sites. The length of this vector should be
Expand Down Expand Up @@ -544,7 +538,6 @@ impl<'tcx> TerminatorKind<'tcx> {
use self::TerminatorKind::*;
match *self {
Goto { target: ref b } => slice::ref_slice(b).into_cow(),
Switch { targets: ref b, .. } => b[..].into_cow(),
SwitchInt { targets: ref b, .. } => b[..].into_cow(),
Resume => (&[]).into_cow(),
Return => (&[]).into_cow(),
Expand Down Expand Up @@ -573,7 +566,6 @@ impl<'tcx> TerminatorKind<'tcx> {
use self::TerminatorKind::*;
match *self {
Goto { target: ref mut b } => vec![b],
Switch { targets: ref mut b, .. } => b.iter_mut().collect(),
SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
Resume => Vec::new(),
Return => Vec::new(),
Expand Down Expand Up @@ -651,7 +643,6 @@ impl<'tcx> TerminatorKind<'tcx> {
use self::TerminatorKind::*;
match *self {
Goto { .. } => write!(fmt, "goto"),
Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv),
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
Return => write!(fmt, "return"),
Resume => write!(fmt, "resume"),
Expand Down Expand Up @@ -701,12 +692,6 @@ impl<'tcx> TerminatorKind<'tcx> {
match *self {
Return | Resume | Unreachable => vec![],
Goto { .. } => vec!["".into()],
Switch { ref adt_def, .. } => {
adt_def.variants
.iter()
.map(|variant| variant.name.to_string().into())
.collect()
}
SwitchInt { ref values, .. } => {
values.iter()
.map(|const_val| {
Expand Down
12 changes: 9 additions & 3 deletions src/librustc/mir/tcx.rs
Expand Up @@ -17,6 +17,7 @@ use mir::*;
use ty::subst::{Subst, Substs};
use ty::{self, AdtDef, Ty, TyCtxt};
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use syntax::attr;
use hir;

#[derive(Copy, Clone, Debug)]
Expand Down Expand Up @@ -170,9 +171,14 @@ impl<'tcx> Rvalue<'tcx> {
Some(operand.ty(mir, tcx))
}
Rvalue::Discriminant(ref lval) => {
if let ty::TyAdt(_, _) = lval.ty(mir, tcx).to_ty(tcx).sty {
// TODO
None
if let ty::TyAdt(adt_def, _) = lval.ty(mir, tcx).to_ty(tcx).sty {
// FIXME: Why this does not work?
// Some(adt_def.discr_ty.to_ty(tcx))
let ty = match adt_def.discr_ty {
attr::SignedInt(i) => tcx.mk_mach_int(i),
attr::UnsignedInt(i) => tcx.mk_mach_uint(i),
};
Some(ty)
} else {
None
}
Expand Down
9 changes: 0 additions & 9 deletions src/librustc/mir/visit.rs
Expand Up @@ -362,15 +362,6 @@ macro_rules! make_mir_visitor {
self.visit_branch(block, target);
}

TerminatorKind::Switch { ref $($mutability)* discr,
adt_def: _,
ref targets } => {
self.visit_lvalue(discr, LvalueContext::Inspect, source_location);
for &target in targets {
self.visit_branch(block, target);
}
}

TerminatorKind::SwitchInt { ref $($mutability)* discr,
ref $($mutability)* switch_ty,
ref $($mutability)* values,
Expand Down
20 changes: 5 additions & 15 deletions src/librustc/ty/util.rs
Expand Up @@ -39,27 +39,17 @@ use rustc_i128::i128;
use hir;

pub trait IntTypeExt {
fn to_ty<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx>;
fn to_ty<'a, 'tcx>(self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx>;
fn disr_incr<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, val: Option<Disr>)
-> Option<Disr>;
fn initial_discriminant<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>) -> Disr;
}

impl IntTypeExt for attr::IntType {
fn to_ty<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> {
match *self {
SignedInt(ast::IntTy::I8) => tcx.types.i8,
SignedInt(ast::IntTy::I16) => tcx.types.i16,
SignedInt(ast::IntTy::I32) => tcx.types.i32,
SignedInt(ast::IntTy::I64) => tcx.types.i64,
SignedInt(ast::IntTy::I128) => tcx.types.i128,
SignedInt(ast::IntTy::Is) => tcx.types.isize,
UnsignedInt(ast::UintTy::U8) => tcx.types.u8,
UnsignedInt(ast::UintTy::U16) => tcx.types.u16,
UnsignedInt(ast::UintTy::U32) => tcx.types.u32,
UnsignedInt(ast::UintTy::U64) => tcx.types.u64,
UnsignedInt(ast::UintTy::U128) => tcx.types.u128,
UnsignedInt(ast::UintTy::Us) => tcx.types.usize,
fn to_ty<'a, 'gcx, 'tcx>(self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
match self {
SignedInt(i) => tcx.mk_mach_int(i),
UnsignedInt(i) => tcx.mk_mach_uint(i),
}
}

Expand Down
1 change: 0 additions & 1 deletion src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
Expand Up @@ -454,7 +454,6 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
self.propagate_bits_into_entry_set_for(in_out, changed, target);
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
}
mir::TerminatorKind::Switch { ref targets, .. } |
mir::TerminatorKind::SwitchInt { ref targets, .. } => {
for target in targets {
self.propagate_bits_into_entry_set_for(in_out, changed, target);
Expand Down
49 changes: 34 additions & 15 deletions src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
Expand Up @@ -17,9 +17,10 @@ use super::{DropFlagState, MoveDataParamEnv};
use super::patch::MirPatch;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::ty::util::IntTypeExt;
use rustc::mir::*;
use rustc::mir::transform::{Pass, MirPass, MirSource};
use rustc::middle::const_val::ConstVal;
use rustc::middle::const_val::{ConstVal, ConstInt};
use rustc::middle::lang_items;
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_set::IdxSetBuf;
Expand Down Expand Up @@ -672,27 +673,45 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
self.drop_ladder(c, fields)
}
_ => {
let variant_drops : Vec<BasicBlock> =
(0..adt.variants.len()).map(|i| {
self.open_drop_for_variant(c, &mut drop_block,
adt, substs, i)
}).collect();

let mut values = Vec::with_capacity(adt.variants.len());
let mut blocks = Vec::with_capacity(adt.variants.len() + 1);
for (idx, variant) in adt.variants.iter().enumerate() {
let discr = ConstInt::new_inttype(variant.disr_val, adt.discr_ty,
self.tcx.sess.target.uint_type,
self.tcx.sess.target.int_type).unwrap();
values.push(ConstVal::Integral(discr));
blocks.push(self.open_drop_for_variant(c, &mut drop_block, adt, substs, idx));
}
// If there are multiple variants, then if something
// is present within the enum the discriminant, tracked
// by the rest path, must be initialized.
//
// Additionally, we do not want to switch on the
// discriminant after it is free-ed, because that
// way lies only trouble.

let switch_block = self.new_block(
c, c.is_cleanup, TerminatorKind::Switch {
discr: c.lvalue.clone(),
adt_def: adt,
targets: variant_drops
});

let discr_ty = adt.discr_ty.to_ty(self.tcx);
let discr = Lvalue::Local(self.patch.new_temp(discr_ty));
let switch_block = self.patch.new_block(BasicBlockData {
statements: vec![
Statement {
source_info: c.source_info,
kind: StatementKind::Assign(discr.clone(),
Rvalue::Discriminant(c.lvalue.clone()))
}
],
terminator: Some(Terminator {
source_info: c.source_info,
kind: TerminatorKind::SwitchInt {
discr: Operand::Consume(discr),
switch_ty: discr_ty,
values: values,
targets: blocks,
// adt_def: adt,
// targets: variant_drops
}
}),
is_cleanup: c.is_cleanup,
});
self.drop_flag_test_block(c, switch_block)
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_borrowck/borrowck/mir/gather_moves.rs
Expand Up @@ -465,8 +465,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
}

TerminatorKind::Assert { .. } |
TerminatorKind::SwitchInt { .. } |
TerminatorKind::Switch { .. } => {
TerminatorKind::SwitchInt { .. } => {
// branching terminators - these don't move anything
}

Expand Down
8 changes: 8 additions & 0 deletions src/librustc_const_math/int.rs
Expand Up @@ -77,6 +77,14 @@ mod ibounds {
}

impl ConstInt {
pub fn new_inttype(val: u128, ty: IntType, usize_ty: UintTy, isize_ty: IntTy)
-> Option<ConstInt> {
match ty {
IntType::SignedInt(i) => ConstInt::new_signed(val as i128, i, isize_ty),
IntType::UnsignedInt(i) => ConstInt::new_unsigned(val, i, usize_ty),
}
}

/// Creates a new unsigned ConstInt with matching type while also checking that overflow does
/// not happen.
pub fn new_unsigned(val: u128, ty: UintTy, usize_ty: UintTy) -> Option<ConstInt> {
Expand Down
4 changes: 4 additions & 0 deletions src/librustc_data_structures/bitvec.rs
Expand Up @@ -30,6 +30,10 @@ impl BitVector {
}
}

pub fn count(&self) -> usize {
self.data.iter().map(|e| e.count_ones() as usize).sum()
}

#[inline]
pub fn contains(&self, bit: usize) -> bool {
let (word, mask) = word_mask(bit);
Expand Down
52 changes: 40 additions & 12 deletions src/librustc_mir/build/matches/test.rs
Expand Up @@ -20,11 +20,12 @@ use build::matches::{Candidate, MatchPair, Test, TestKind};
use hair::*;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::bitvec::BitVector;
use rustc::middle::const_val::ConstVal;
use rustc::middle::const_val::{ConstVal, ConstInt};
use rustc::ty::{self, Ty};
use rustc::mir::*;
use rustc::hir::RangeEnd;
use syntax_pos::Span;
use syntax::attr;
use std::cmp::Ordering;

impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
Expand Down Expand Up @@ -182,24 +183,51 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let source_info = self.source_info(test.span);
match test.kind {
TestKind::Switch { adt_def, ref variants } => {
// Variants is a BitVec of indexes into adt_def.variants.
let num_enum_variants = self.hir.num_variants(adt_def);
let used_variants = variants.count();
let mut otherwise_block = None;
let target_blocks: Vec<_> = (0..num_enum_variants).map(|i| {
if variants.contains(i) {
self.cfg.start_new_block()
let mut target_blocks = Vec::with_capacity(num_enum_variants);
let mut targets = Vec::with_capacity(used_variants + 1);
let mut values = Vec::with_capacity(used_variants);
let tcx = self.hir.tcx();
for (idx, variant) in adt_def.variants.iter().enumerate() {
target_blocks.place_back() <- if variants.contains(idx) {
let discr = ConstInt::new_inttype(variant.disr_val, adt_def.discr_ty,
tcx.sess.target.uint_type,
tcx.sess.target.int_type).unwrap();
values.push(ConstVal::Integral(discr));
*(targets.place_back() <- 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, source_info, TerminatorKind::Switch {
discr: lvalue.clone(),
adt_def: adt_def,
targets: target_blocks.clone()
};
}
if let Some(otherwise_block) = otherwise_block {
targets.push(otherwise_block);
} else {
values.pop();
}
debug!("num_enum_variants: {}, tested variants: {:?}, variants: {:?}",
num_enum_variants, values, variants);
// FIXME: WHY THIS DOES NOT WORK?!
// let discr_ty = adt_def.discr_ty.to_ty(tcx);
let discr_ty = match adt_def.discr_ty {
attr::SignedInt(i) => tcx.mk_mach_int(i),
attr::UnsignedInt(i) => tcx.mk_mach_uint(i),
};

let discr = self.temp(discr_ty);
self.cfg.push_assign(block, source_info, &discr,
Rvalue::Discriminant(lvalue.clone()));
assert_eq!(values.len() + 1, targets.len());
self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: Operand::Consume(discr),
switch_ty: discr_ty,
values: values,
targets: targets
});
target_blocks
}
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_mir/lib.rs
Expand Up @@ -26,6 +26,8 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
#![feature(rustc_diagnostic_macros)]
#![feature(rustc_private)]
#![feature(staged_api)]
#![feature(placement_in_syntax)]
#![feature(collection_placement)]

#[macro_use] extern crate log;
extern crate graphviz as dot;
Expand Down
1 change: 0 additions & 1 deletion src/librustc_mir/transform/no_landing_pads.rs
Expand Up @@ -28,7 +28,6 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::Unreachable |
TerminatorKind::Switch { .. } |
TerminatorKind::SwitchInt { .. } => {
/* nothing to do */
},
Expand Down
1 change: 0 additions & 1 deletion src/librustc_mir/transform/qualify_consts.rs
Expand Up @@ -394,7 +394,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
return Qualif::empty();
}

TerminatorKind::Switch {..} |
TerminatorKind::SwitchInt {..} |
TerminatorKind::DropAndReplace { .. } |
TerminatorKind::Resume |
Expand Down
1 change: 0 additions & 1 deletion src/librustc_mir/transform/simplify.rs
Expand Up @@ -209,7 +209,6 @@ impl<'a, 'tcx: 'a> CfgSimplifier<'a, 'tcx> {
// turn a branch with all successors identical to a goto
fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool {
match terminator.kind {
TerminatorKind::Switch { .. } |
TerminatorKind::SwitchInt { .. } => {},
_ => return false
};
Expand Down
14 changes: 0 additions & 14 deletions src/librustc_mir/transform/type_check.rs
Expand Up @@ -436,19 +436,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
// FIXME: check the values
}
TerminatorKind::Switch { ref discr, adt_def, ref targets } => {
let discr_ty = discr.ty(mir, tcx).to_ty(tcx);
match discr_ty.sty {
ty::TyAdt(def, _) if def.is_enum() &&
def == adt_def &&
adt_def.variants.len() == targets.len()
=> {},
_ => {
span_mirbug!(self, term, "bad Switch ({:?} on {:?})",
adt_def, discr_ty);
}
}
}
TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
let func_ty = func.ty(mir, tcx);
debug!("check_terminator: call, func_ty={:?}", func_ty);
Expand Down Expand Up @@ -593,7 +580,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
match block.terminator().kind {
TerminatorKind::Goto { target } =>
self.assert_iscleanup(mir, block, target, is_cleanup),
TerminatorKind::Switch { ref targets, .. } |
TerminatorKind::SwitchInt { ref targets, .. } => {
for target in targets {
self.assert_iscleanup(mir, block, *target, is_cleanup);
Expand Down

0 comments on commit aac82d9

Please sign in to comment.