Only SwitchInt over integers, not all consts

Also use a Cow to avoid full Vec for all SwitchInts
This commit is contained in:
Simonas Kazlauskas 2017-02-02 06:44:30 +02:00
parent aac82d9b13
commit a00a0adc79
10 changed files with 154 additions and 101 deletions

View File

@ -11,6 +11,7 @@
use syntax::symbol::InternedString;
use syntax::ast;
use std::rc::Rc;
use std::borrow::Cow;
use hir::def_id::DefId;
use rustc_const_math::*;
use self::ConstVal::*;
@ -18,6 +19,8 @@ pub use rustc_const_math::ConstInt;
use std::collections::BTreeMap;
pub static BOOL_SWITCH_TRUE: Cow<'static, [ConstInt]> = Cow::Borrowed(&[ConstInt::Infer(1)]);
#[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
pub enum ConstVal {
Float(ConstFloat),
@ -49,4 +52,14 @@ impl ConstVal {
Char(..) => "char",
}
}
pub fn to_const_int(&self) -> Option<ConstInt> {
match *self {
ConstVal::Integral(i) => Some(i),
ConstVal::Bool(true) => Some(ConstInt::Infer(1)),
ConstVal::Bool(false) => Some(ConstInt::Infer(0)),
ConstVal::Char(ch) => Some(ConstInt::U32(ch as u32)),
_ => None
}
}
}

View File

@ -446,6 +446,9 @@ pub struct Terminator<'tcx> {
pub kind: TerminatorKind<'tcx>
}
/// For use in SwitchInt, for switching on bools.
pub static BOOL_SWITCH_TRUE: Cow<'static, [ConstInt]> = Cow::Borrowed(&[ConstInt::Infer(1)]);
#[derive(Clone, RustcEncodable, RustcDecodable)]
pub enum TerminatorKind<'tcx> {
/// block should have one successor in the graph; we jump there
@ -464,8 +467,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 doesnt quite make any sense here? Its a Switch*Int*.
values: Vec<ConstVal>,
values: Cow<'tcx, [ConstInt]>,
/// Possible branch sites. The length of this vector should be
/// equal to the length of the `values` vector plus 1 -- the
@ -696,7 +698,7 @@ impl<'tcx> TerminatorKind<'tcx> {
values.iter()
.map(|const_val| {
let mut buf = String::new();
fmt_const_val(&mut buf, const_val).unwrap();
fmt_const_val(&mut buf, &ConstVal::Integral(*const_val)).unwrap();
buf.into()
})
.chain(iter::once(String::from("otherwise").into()))

View File

@ -223,6 +223,12 @@ macro_rules! make_mir_visitor {
self.super_const_val(const_val);
}
fn visit_const_int(&mut self,
const_int: &ConstInt,
_: Location) {
self.super_const_int(const_int);
}
fn visit_const_usize(&mut self,
const_usize: & $($mutability)* ConstUsize,
_: Location) {
@ -364,12 +370,12 @@ macro_rules! make_mir_visitor {
TerminatorKind::SwitchInt { ref $($mutability)* discr,
ref $($mutability)* switch_ty,
ref $($mutability)* values,
ref values,
ref targets } => {
self.visit_operand(discr, source_location);
self.visit_ty(switch_ty);
for value in values {
self.visit_const_val(value, source_location);
for value in &values[..] {
self.visit_const_int(value, source_location);
}
for &target in targets {
self.visit_branch(block, target);
@ -698,10 +704,13 @@ macro_rules! make_mir_visitor {
_substs: & $($mutability)* ClosureSubsts<'tcx>) {
}
fn super_const_val(&mut self, _substs: & $($mutability)* ConstVal) {
fn super_const_val(&mut self, _const_val: & $($mutability)* ConstVal) {
}
fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) {
fn super_const_int(&mut self, _const_int: &ConstInt) {
}
fn super_const_usize(&mut self, _const_usize: & $($mutability)* ConstUsize) {
}
// Convenience methods

View File

@ -679,7 +679,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
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));
values.push(discr);
blocks.push(self.open_drop_for_variant(c, &mut drop_block, adt, substs, idx));
}
// If there are multiple variants, then if something
@ -704,7 +704,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
kind: TerminatorKind::SwitchInt {
discr: Operand::Consume(discr),
switch_ty: discr_ty,
values: values,
values: From::from(values),
targets: blocks,
// adt_def: adt,
// targets: variant_drops
@ -836,7 +836,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
self.new_block(c, is_cleanup, TerminatorKind::SwitchInt {
discr: Operand::Consume(flag),
switch_ty: boolty,
values: vec![ConstVal::Bool(true)],
values: BOOL_SWITCH_TRUE.clone(),
targets: vec![on_set, on_unset],
})
}

View File

@ -15,7 +15,6 @@ use build::expr::category::{Category, RvalueFunc};
use hair::*;
use rustc::ty;
use rustc::mir::*;
use rustc::middle::const_val::ConstVal;
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// Compile `expr`, storing the result into `destination`, which
@ -73,7 +72,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: operand,
switch_ty: this.hir.bool_ty(),
values: vec![ConstVal::Bool(true)],
values: BOOL_SWITCH_TRUE.clone(),
targets: vec![then_block, else_block],
});
@ -120,7 +119,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
this.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: lhs,
switch_ty: this.hir.bool_ty(),
values: vec![ConstVal::Bool(true)],
values: BOOL_SWITCH_TRUE.clone(),
targets: blocks,
});
@ -128,7 +127,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
this.cfg.terminate(else_block, source_info, TerminatorKind::SwitchInt {
discr: rhs,
switch_ty: this.hir.bool_ty(),
values: vec![ConstVal::Bool(true)],
values: BOOL_SWITCH_TRUE.clone(),
targets: vec![true_block, false_block],
});
@ -192,7 +191,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
TerminatorKind::SwitchInt {
discr: cond,
switch_ty: this.hir.bool_ty(),
values: vec![ConstVal::Bool(true)],
values: BOOL_SWITCH_TRUE.clone(),
targets: vec![body_block, exit_block],
});

View File

@ -675,7 +675,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: cond,
switch_ty: self.hir.bool_ty(),
values: vec![ConstVal::Bool(true)],
values: BOOL_SWITCH_TRUE.clone(),
targets: vec![arm_block, otherwise],
});
Some(otherwise)

View File

@ -196,7 +196,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
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));
values.push(discr);
*(targets.place_back() <- self.cfg.start_new_block())
} else {
if otherwise_block.is_none() {
@ -226,59 +226,45 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: Operand::Consume(discr),
switch_ty: discr_ty,
values: values,
values: From::from(values),
targets: targets
});
target_blocks
}
TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
let (targets, term) = match switch_ty.sty {
// If we're matching on boolean we can
// use the If TerminatorKind instead
ty::TyBool => {
assert!(options.len() > 0 && options.len() <= 2);
let (true_bb, else_bb) =
(self.cfg.start_new_block(),
self.cfg.start_new_block());
let targets = match &options[0] {
&ConstVal::Bool(true) => vec![true_bb, else_bb],
&ConstVal::Bool(false) => vec![else_bb, true_bb],
v => span_bug!(test.span, "expected boolean value but got {:?}", v)
};
(targets, TerminatorKind::SwitchInt {
discr: Operand::Consume(lvalue.clone()),
switch_ty: self.hir.bool_ty(),
values: vec![ConstVal::Bool(true)],
targets: vec![true_bb, else_bb]
})
}
_ => {
// The switch may be inexhaustive so we
// add a catch all block
let otherwise = self.cfg.start_new_block();
let targets: Vec<_> =
options.iter()
.map(|_| self.cfg.start_new_block())
.chain(Some(otherwise))
.collect();
(targets.clone(),
TerminatorKind::SwitchInt {
discr: Operand::Consume(lvalue.clone()),
switch_ty: switch_ty,
values: options.clone(),
targets: targets
})
}
let (values, targets, ret) = if switch_ty.sty == ty::TyBool {
assert!(options.len() > 0 && options.len() <= 2);
let (true_bb, false_bb) = (self.cfg.start_new_block(),
self.cfg.start_new_block());
let ret = match &options[0] {
&ConstVal::Bool(true) => vec![true_bb, false_bb],
&ConstVal::Bool(false) => vec![false_bb, true_bb],
v => span_bug!(test.span, "expected boolean value but got {:?}", v)
};
(BOOL_SWITCH_TRUE.clone(), vec![true_bb, false_bb], ret)
} else {
// The switch may be inexhaustive so we
// add a catch all block
let otherwise = self.cfg.start_new_block();
let targets: Vec<_> =
options.iter()
.map(|_| self.cfg.start_new_block())
.chain(Some(otherwise))
.collect();
let values: Vec<_> = options.iter().map(|v|
v.to_const_int().expect("switching on integral")
).collect();
(From::from(values), targets.clone(), targets)
};
self.cfg.terminate(block, source_info, term);
targets
self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: Operand::Consume(lvalue.clone()),
switch_ty: switch_ty,
values: values,
targets: targets.clone(),
});
ret
}
TestKind::Eq { ref value, mut ty } => {
@ -346,10 +332,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.cfg.terminate(eq_block, source_info, TerminatorKind::SwitchInt {
discr: Operand::Consume(eq_result),
switch_ty: self.hir.bool_ty(),
values: vec![ConstVal::Bool(true)],
values: BOOL_SWITCH_TRUE.clone(),
targets: vec![block, fail],
});
vec![block, fail]
} else {
let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val);
@ -391,16 +376,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
Operand::Consume(expected)));
// branch based on result
let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
self.cfg.start_new_block()];
let (false_bb, true_bb) = (self.cfg.start_new_block(),
self.cfg.start_new_block());
self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: Operand::Consume(result),
switch_ty: self.hir.bool_ty(),
values: vec![ConstVal::Bool(true)],
targets: target_blocks.clone(),
values: BOOL_SWITCH_TRUE.clone(),
targets: vec![true_bb, false_bb],
});
target_blocks
vec![true_bb, false_bb]
}
}
}
@ -425,10 +409,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.cfg.terminate(block, source_info, TerminatorKind::SwitchInt {
discr: Operand::Consume(result),
switch_ty: self.hir.bool_ty(),
values: vec![ConstVal::Bool(true)],
values: BOOL_SWITCH_TRUE.clone(),
targets: vec![target_block, fail_block]
});
target_block
}

View File

@ -11,6 +11,7 @@
use llvm::{self, ValueRef, BasicBlockRef};
use rustc_const_eval::{ErrKind, ConstEvalErr, note_const_eval_err};
use rustc::middle::lang_items;
use rustc::middle::const_val::ConstInt;
use rustc::ty::{self, layout, TypeFoldable};
use rustc::mir;
use abi::{Abi, FnType, ArgType};
@ -134,14 +135,24 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
}
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();
let discr = self.trans_operand(&bcx, discr).immediate();
let switch = bcx.switch(discr, llblock(self, *otherwise), values.len());
for (value, target) in values.iter().zip(targets) {
let val = Const::from_constval(bcx.ccx, value.clone(), switch_ty);
let llbb = llblock(self, *target);
bcx.add_case(switch, val.llval, llbb)
let discr = self.trans_operand(&bcx, discr);
if switch_ty == bcx.tcx().types.bool {
let lltrue = llblock(self, targets[0]);
let llfalse = llblock(self, targets[1]);
if let [ConstInt::Infer(0)] = values[..] {
bcx.cond_br(discr.immediate(), llfalse, lltrue);
} else {
bcx.cond_br(discr.immediate(), lltrue, llfalse);
}
} else {
let (otherwise, targets) = targets.split_last().unwrap();
let switch = bcx.switch(discr.immediate(),
llblock(self, *otherwise), values.len());
for (value, target) in values.iter().zip(targets) {
let val = Const::from_constint(bcx.ccx, value);
let llbb = llblock(self, *target);
bcx.add_case(switch, val.llval, llbb)
}
}
}

View File

@ -61,6 +61,33 @@ impl<'tcx> Const<'tcx> {
}
}
pub fn from_constint<'a>(ccx: &CrateContext<'a, 'tcx>, ci: &ConstInt)
-> Const<'tcx> {
let tcx = ccx.tcx();
let (llval, ty) = match *ci {
I8(v) => (C_integral(Type::i8(ccx), v as u64, true), tcx.types.i8),
I16(v) => (C_integral(Type::i16(ccx), v as u64, true), tcx.types.i16),
I32(v) => (C_integral(Type::i32(ccx), v as u64, true), tcx.types.i32),
I64(v) => (C_integral(Type::i64(ccx), v as u64, true), tcx.types.i64),
I128(v) => (C_big_integral(Type::i128(ccx), v as u128), tcx.types.i128),
Isize(v) => {
let i = v.as_i64(ccx.tcx().sess.target.int_type);
(C_integral(Type::int(ccx), i as u64, true), tcx.types.isize)
},
U8(v) => (C_integral(Type::i8(ccx), v as u64, false), tcx.types.u8),
U16(v) => (C_integral(Type::i16(ccx), v as u64, false), tcx.types.u16),
U32(v) => (C_integral(Type::i32(ccx), v as u64, false), tcx.types.u32),
U64(v) => (C_integral(Type::i64(ccx), v, false), tcx.types.u64),
U128(v) => (C_big_integral(Type::i128(ccx), v), tcx.types.u128),
Usize(v) => {
let u = v.as_u64(ccx.tcx().sess.target.uint_type);
(C_integral(Type::int(ccx), u, false), tcx.types.usize)
},
Infer(_) | InferSigned(_) => bug!("MIR must not use `{:?}`", ci),
};
Const { llval: llval, ty: ty }
}
/// Translate ConstVal into a LLVM constant value.
pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>,
cv: ConstVal,
@ -72,26 +99,7 @@ impl<'tcx> Const<'tcx> {
ConstVal::Float(F64(v)) => C_floating_f64(v, llty),
ConstVal::Float(FInfer {..}) => bug!("MIR must not use `{:?}`", cv),
ConstVal::Bool(v) => C_bool(ccx, v),
ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true),
ConstVal::Integral(I16(v)) => C_integral(Type::i16(ccx), v as u64, true),
ConstVal::Integral(I32(v)) => C_integral(Type::i32(ccx), v as u64, true),
ConstVal::Integral(I64(v)) => C_integral(Type::i64(ccx), v as u64, true),
ConstVal::Integral(I128(v)) => C_big_integral(Type::i128(ccx), v as u128),
ConstVal::Integral(Isize(v)) => {
let i = v.as_i64(ccx.tcx().sess.target.int_type);
C_integral(Type::int(ccx), i as u64, true)
},
ConstVal::Integral(U8(v)) => C_integral(Type::i8(ccx), v as u64, false),
ConstVal::Integral(U16(v)) => C_integral(Type::i16(ccx), v as u64, false),
ConstVal::Integral(U32(v)) => C_integral(Type::i32(ccx), v as u64, false),
ConstVal::Integral(U64(v)) => C_integral(Type::i64(ccx), v, false),
ConstVal::Integral(U128(v)) => C_big_integral(Type::i128(ccx), v),
ConstVal::Integral(Usize(v)) => {
let u = v.as_u64(ccx.tcx().sess.target.uint_type);
C_integral(Type::int(ccx), u, false)
},
ConstVal::Integral(Infer(_)) |
ConstVal::Integral(InferSigned(_)) => bug!("MIR must not use `{:?}`", cv),
ConstVal::Integral(ref i) => return Const::from_constint(ccx, i),
ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
ConstVal::Struct(_) | ConstVal::Tuple(_) |

View File

@ -567,6 +567,34 @@ impl<T:Decodable> Decodable for Vec<T> {
}
}
impl<'a, T:Encodable> Encodable for Cow<'a, [T]>
where [T]: ToOwned<Owned = Vec<T>>
{
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_seq(self.len(), |s| {
for (i, e) in self.iter().enumerate() {
s.emit_seq_elt(i, |s| e.encode(s))?
}
Ok(())
})
}
}
impl<T:Decodable+ToOwned> Decodable for Cow<'static, [T]>
where [T]: ToOwned<Owned = Vec<T>>
{
fn decode<D: Decoder>(d: &mut D) -> Result<Cow<'static, [T]>, D::Error> {
d.read_seq(|d, len| {
let mut v = Vec::with_capacity(len);
for i in 0..len {
v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?);
}
Ok(Cow::Owned(v))
})
}
}
impl<T:Encodable> Encodable for Option<T> {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_option(|s| {