SwitchInt over Switch
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.
This commit is contained in:
parent
5d70a7fbe4
commit
aac82d9b13
19 changed files with 105 additions and 130 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -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
|
||||
|
|
@ -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(),
|
||||
|
|
@ -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(),
|
||||
|
|
@ -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"),
|
||||
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -672,12 +673,15 @@ 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.
|
||||
|
|
@ -685,14 +689,29 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
|
|||
// 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
|
|||
TerminatorKind::Resume |
|
||||
TerminatorKind::Return |
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::Switch { .. } |
|
||||
TerminatorKind::SwitchInt { .. } => {
|
||||
/* nothing to do */
|
||||
},
|
||||
|
|
|
|||
|
|
@ -394,7 +394,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
|
|||
return Qualif::empty();
|
||||
}
|
||||
|
||||
TerminatorKind::Switch {..} |
|
||||
TerminatorKind::SwitchInt {..} |
|
||||
TerminatorKind::DropAndReplace { .. } |
|
||||
TerminatorKind::Resume |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -148,7 +148,6 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
|
|||
self.record("TerminatorKind", kind);
|
||||
self.record(match *kind {
|
||||
TerminatorKind::Goto { .. } => "TerminatorKind::Goto",
|
||||
TerminatorKind::Switch { .. } => "TerminatorKind::Switch",
|
||||
TerminatorKind::SwitchInt { .. } => "TerminatorKind::SwitchInt",
|
||||
TerminatorKind::Resume => "TerminatorKind::Resume",
|
||||
TerminatorKind::Return => "TerminatorKind::Return",
|
||||
|
|
|
|||
|
|
@ -204,7 +204,6 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock
|
|||
TerminatorKind::Resume |
|
||||
TerminatorKind::Return |
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::Switch { .. } |
|
||||
TerminatorKind::SwitchInt { .. } => {
|
||||
/* nothing to do */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,12 @@ use rustc::middle::lang_items;
|
|||
use rustc::ty::{self, layout, TypeFoldable};
|
||||
use rustc::mir;
|
||||
use abi::{Abi, FnType, ArgType};
|
||||
use adt;
|
||||
use base::{self, Lifetime};
|
||||
use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual};
|
||||
use builder::Builder;
|
||||
use common::{self, Funclet};
|
||||
use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef};
|
||||
use consts;
|
||||
use Disr;
|
||||
use machine::{llalign_of_min, llbitsize_of_real};
|
||||
use meth;
|
||||
use type_of::{self, align_of};
|
||||
|
|
@ -29,7 +27,6 @@ use glue;
|
|||
use type_::Type;
|
||||
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use syntax::symbol::Symbol;
|
||||
|
||||
use std::cmp;
|
||||
|
|
@ -136,41 +133,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
|
|||
funclet_br(self, bcx, target);
|
||||
}
|
||||
|
||||
mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => {
|
||||
let discr_lvalue = self.trans_lvalue(&bcx, discr);
|
||||
let ty = discr_lvalue.ty.to_ty(bcx.tcx());
|
||||
let discr = adt::trans_get_discr(
|
||||
&bcx, ty, discr_lvalue.llval, discr_lvalue.alignment,
|
||||
None, true);
|
||||
|
||||
let mut bb_hist = FxHashMap();
|
||||
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), llblock(self, bb))
|
||||
}
|
||||
// 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, targets.len());
|
||||
assert_eq!(adt_def.variants.len(), targets.len());
|
||||
for (adt_variant, &target) in adt_def.variants.iter().zip(targets) {
|
||||
if default_bb != Some(target) {
|
||||
let llbb = llblock(self, target);
|
||||
let llval = adt::trans_case(&bcx, ty, Disr::from(adt_variant.disr_val));
|
||||
bcx.add_case(switch, llval, llbb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
|
||||
// TODO: cond_br if only 1 value
|
||||
let (otherwise, targets) = targets.split_last().unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue