1
Fork 0

remove our array drop glue and use rustc's instead; implement the new Offset and SizeOf operators

This commit is contained in:
Ralf Jung 2017-06-01 17:24:21 -07:00
parent 14848b3bad
commit 31cf66d0e8
7 changed files with 52 additions and 216 deletions

View file

@ -4,7 +4,6 @@ use std::fmt::Write;
use rustc::hir::def_id::DefId;
use rustc::hir::map::definitions::DefPathData;
use rustc::middle::const_val::ConstVal;
use rustc_const_math::{ConstInt, ConstUsize};
use rustc::mir;
use rustc::traits::Reveal;
use rustc::ty::layout::{self, Layout, Size};
@ -15,7 +14,6 @@ use rustc_data_structures::indexed_vec::Idx;
use syntax::codemap::{self, DUMMY_SP, Span};
use syntax::ast;
use syntax::abi::Abi;
use syntax::symbol::Symbol;
use error::{EvalError, EvalResult};
use lvalue::{Global, GlobalId, Lvalue, LvalueExtra};
@ -43,9 +41,6 @@ pub struct EvalContext<'a, 'tcx: 'a> {
/// This prevents infinite loops and huge computations from freezing up const eval.
/// Remove once halting problem is solved.
pub(crate) steps_remaining: u64,
/// Drop glue for arrays and slices
pub(crate) seq_drop_glue: &'tcx mir::Mir<'tcx>,
}
/// A stack frame.
@ -127,180 +122,6 @@ impl Default for ResourceLimits {
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self {
// Register array drop glue code
let source_info = mir::SourceInfo {
span: DUMMY_SP,
scope: mir::ARGUMENT_VISIBILITY_SCOPE
};
// i = 0; len = Len(*a0); goto head;
let start_block = mir::BasicBlockData {
statements: vec![
mir::Statement {
source_info,
kind: mir::StatementKind::Assign(
mir::Lvalue::Local(mir::Local::new(2)),
mir::Rvalue::Use(mir::Operand::Constant(Box::new(mir::Constant {
span: DUMMY_SP,
ty: tcx.types.usize,
literal: mir::Literal::Value {
value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())),
},
})))
)
},
mir::Statement {
source_info,
kind: mir::StatementKind::Assign(
mir::Lvalue::Local(mir::Local::new(3)),
mir::Rvalue::Len(mir::Lvalue::Projection(Box::new(mir::LvalueProjection {
base: mir::Lvalue::Local(mir::Local::new(1)),
elem: mir::ProjectionElem::Deref,
}))),
)
},
],
terminator: Some(mir::Terminator {
source_info: source_info,
kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) },
}),
is_cleanup: false
};
// head: done = i == len; switch done { 1 => ret, 0 => loop }
let head = mir::BasicBlockData {
statements: vec![
mir::Statement {
source_info,
kind: mir::StatementKind::Assign(
mir::Lvalue::Local(mir::Local::new(4)),
mir::Rvalue::BinaryOp(
mir::BinOp::Eq,
mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))),
mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(3))),
)
)
},
],
terminator: Some(mir::Terminator {
source_info: source_info,
kind: mir::TerminatorKind::SwitchInt {
targets: vec![
mir::BasicBlock::new(2),
mir::BasicBlock::new(4),
],
discr: mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(4))),
switch_ty: tcx.types.bool,
values: vec![ConstInt::U8(0)].into(),
},
}),
is_cleanup: false
};
// loop: drop (*a0)[i]; goto inc;
let loop_ = mir::BasicBlockData {
statements: Vec::new(),
terminator: Some(mir::Terminator {
source_info: source_info,
kind: mir::TerminatorKind::Drop {
target: mir::BasicBlock::new(3),
unwind: None,
location: mir::Lvalue::Projection(Box::new(
mir::LvalueProjection {
base: mir::Lvalue::Projection(Box::new(
mir::LvalueProjection {
base: mir::Lvalue::Local(mir::Local::new(1)),
elem: mir::ProjectionElem::Deref,
}
)),
elem: mir::ProjectionElem::Index(mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2)))),
}
)),
},
}),
is_cleanup: false
};
// inc: i++; goto head;
let inc = mir::BasicBlockData {
statements: vec![
mir::Statement {
source_info,
kind: mir::StatementKind::Assign(
mir::Lvalue::Local(mir::Local::new(2)),
mir::Rvalue::BinaryOp(
mir::BinOp::Add,
mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))),
mir::Operand::Constant(Box::new(mir::Constant {
span: DUMMY_SP,
ty: tcx.types.usize,
literal: mir::Literal::Value {
value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(1, tcx.sess.target.uint_type).unwrap())),
},
})),
)
)
},
],
terminator: Some(mir::Terminator {
source_info: source_info,
kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) },
}),
is_cleanup: false
};
// ret: return;
let ret = mir::BasicBlockData {
statements: Vec::new(),
terminator: Some(mir::Terminator {
source_info: source_info,
kind: mir::TerminatorKind::Return,
}),
is_cleanup: false
};
let locals = vec![
mir::LocalDecl {
mutability: mir::Mutability::Mut,
ty: tcx.mk_nil(),
name: None,
source_info,
is_user_variable: false,
},
mir::LocalDecl {
mutability: mir::Mutability::Mut,
ty: tcx.mk_mut_ptr(tcx.mk_slice(tcx.mk_param(0, Symbol::intern("T")))),
name: None,
source_info,
is_user_variable: false,
},
mir::LocalDecl {
mutability: mir::Mutability::Mut,
ty: tcx.types.usize,
name: None,
source_info,
is_user_variable: false,
},
mir::LocalDecl {
mutability: mir::Mutability::Mut,
ty: tcx.types.usize,
name: None,
source_info,
is_user_variable: false,
},
mir::LocalDecl {
mutability: mir::Mutability::Mut,
ty: tcx.types.bool,
name: None,
source_info,
is_user_variable: false,
},
];
let seq_drop_glue = mir::Mir::new(
vec![start_block, head, loop_, inc, ret].into_iter().collect(),
Vec::new().into_iter().collect(), // vis scopes
Vec::new().into_iter().collect(), // promoted
tcx.mk_nil(), // return type
locals.into_iter().collect(),
1, // arg_count
Vec::new(), // upvars
DUMMY_SP,
);
let seq_drop_glue = tcx.alloc_mir(seq_drop_glue);
EvalContext {
tcx,
memory: Memory::new(&tcx.data_layout, limits.memory_size),
@ -308,7 +129,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
stack: Vec::new(),
stack_limit: limits.stack_limit,
steps_remaining: limits.step_limit,
seq_drop_glue: seq_drop_glue,
}
}
@ -631,6 +451,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.write_value(value, dest, dest_ty)?;
}
BinaryOp(mir::BinOp::Offset, ref left, ref right) => {
let pointer_ty = self.operand_ty(left);
let pointee_ty = pointer_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
// FIXME: assuming here that type size is < i64::max_value()
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
let offset = self.eval_operand_to_primval(right)?.to_i128()? as i64;
let ptr = self.eval_operand_to_primval(left)?.to_ptr()?;
let result_ptr = ptr.signed_offset(offset * pointee_size);
self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?;
}
BinaryOp(bin_op, ref left, ref right) => {
// ignore overflow bit, rustc inserts check branches for us
self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)?;
@ -823,8 +655,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
}
NullaryOp(mir::NullOp::SizeOf, _ty) => {
unimplemented!()
NullaryOp(mir::NullOp::SizeOf, ty) => {
let size = self.type_size(ty)?.expect("SizeOf nullary MIR operator called for unsized type");
self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?;
}
Cast(kind, ref operand, cast_ty) => {

View file

@ -362,7 +362,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let n_ptr = self.eval_operand(operand)?;
let usize = self.tcx.types.usize;
let n = self.value_to_primval(n_ptr, usize)?.to_u64()?;
assert!(n < len);
assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len);
let ptr = base_ptr.offset(n * elem_size);
(ptr, LvalueExtra::None)
}

View file

@ -1,6 +1,5 @@
use rustc::mir;
use rustc::ty::{self, Ty};
use rustc::ty::subst::Kind;
use syntax::codemap::Span;
use error::EvalResult;
@ -21,7 +20,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
};
self.drop(val, instance, ty, span)
}
pub(crate) fn drop(&mut self, mut arg: Value, mut instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> {
pub(crate) fn drop(&mut self, arg: Value, mut instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> {
trace!("drop: {:#?}, {:?}, {:?}", arg, ty.sty, instance.def);
if let ty::InstanceDef::DropGlue(_, None) = instance.def {
@ -44,23 +43,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
None => return Ok(()),
}
},
ty::TyArray(elem, n) => {
instance.substs = self.tcx.mk_substs([
Kind::from(elem),
].iter().cloned());
let ptr = match arg {
Value::ByVal(PrimVal::Ptr(src_ptr)) => src_ptr,
_ => bug!("expected thin ptr, got {:?}", arg),
};
arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(n as u128));
self.seq_drop_glue
},
ty::TySlice(elem) => {
instance.substs = self.tcx.mk_substs([
Kind::from(elem),
].iter().cloned());
self.seq_drop_glue
},
_ => self.load_mir(instance.def)?,
};

View file

@ -360,11 +360,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"size_of" => {
let ty = substs.type_at(0);
// FIXME: change the `box_free` lang item to take `T: ?Sized` and have it use the
// `size_of_val` intrinsic, then change this back to
// .expect("size_of intrinsic called on unsized value")
// see https://github.com/rust-lang/rust/pull/37708
let size = self.type_size(ty)?.unwrap_or(!0) as u128;
let size = self.type_size(ty)?.expect("size_of intrinsic called on unsized value") as u128;
self.write_primval(dest, PrimVal::from_u128(size), dest_ty)?;
}

View file

@ -1,6 +1,6 @@
use rustc::hir::def_id::DefId;
use rustc::mir;
use rustc::ty::{self, TypeVariants, Ty, TypeAndMut};
use rustc::ty::{self, TypeVariants, Ty};
use rustc::ty::layout::Layout;
use syntax::codemap::Span;
use syntax::attr;
@ -730,12 +730,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) };
// Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t.
let key_size = match self.operand_ty(&arg_operands[0]).sty {
TypeVariants::TyRawPtr(TypeAndMut { ty, .. }) => {
let layout = self.type_layout(ty)?;
layout.size(&self.tcx.data_layout)
}
_ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))
let key_type = self.operand_ty(&arg_operands[0]).builtin_deref(true, ty::LvaluePreference::NoPreference)
.ok_or(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))?.ty;
let key_size = {
let layout = self.type_layout(key_type)?;
layout.size(&self.tcx.data_layout)
};
// Create key and write it into the memory where key_ptr wants it

View file

@ -1,4 +1,4 @@
struct Bar;
struct Bar(i32); // ZSTs are tested separately
static mut DROP_COUNT: usize = 0;
@ -9,8 +9,13 @@ impl Drop for Bar {
}
fn main() {
let b = [Bar, Bar, Bar, Bar];
let b = [Bar(0), Bar(0), Bar(0), Bar(0)];
assert_eq!(unsafe { DROP_COUNT }, 0);
drop(b);
assert_eq!(unsafe { DROP_COUNT }, 4);
// check empty case
let b : [Bar; 0] = [];
drop(b);
assert_eq!(unsafe { DROP_COUNT }, 4);
}

View file

@ -0,0 +1,21 @@
struct Bar;
static mut DROP_COUNT: usize = 0;
impl Drop for Bar {
fn drop(&mut self) {
unsafe { DROP_COUNT += 1; }
}
}
fn main() {
let b = [Bar, Bar, Bar, Bar];
assert_eq!(unsafe { DROP_COUNT }, 0);
drop(b);
assert_eq!(unsafe { DROP_COUNT }, 4);
// check empty case
let b : [Bar; 0] = [];
drop(b);
assert_eq!(unsafe { DROP_COUNT }, 4);
}