Do not pre-allocate local variables.

Thanks to the `Value` locals refactoring, now primitive locals (ints,
floats, chars, bools, and the like) will not require `Allocation`s at
all, and locals that are never initialized at all because of conditional
control flow won't be wasting memory.
This commit is contained in:
Scott Olson 2016-10-15 23:31:42 -06:00
parent 6c463b7562
commit 49e6c57ef9
5 changed files with 48 additions and 54 deletions

View File

@ -82,7 +82,9 @@ pub struct Frame<'a, 'tcx: 'a> {
/// The list of locals for this stack frame, stored in order as
/// `[arguments..., variables..., temporaries...]`. The locals are stored as `Value`s, which
/// can either directly contain `PrimVal` or refer to some part of an `Allocation`.
pub locals: Vec<Value>,
///
/// Before being initialized, a local is simply marked as None.
pub locals: Vec<Option<Value>>,
////////////////////////////////////////////////////////////////////////////////
// Current position within the function
@ -351,27 +353,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
) -> EvalResult<'tcx, ()> {
::log_settings::settings().indentation += 1;
// Skip 1 because we don't make a slot for the ReturnPointer, which is local number zero.
// The ReturnPointer is represented by `return_lvalue`, and points to an allocation or a
// local in a higher stack frame.
//
// FIXME(solson): Write this in a way that doesn't assume ReturnPointer is local 0.
let local_tys = mir.local_decls.iter().map(|a| a.ty).skip(1);
let locals: EvalResult<'tcx, Vec<Value>> = local_tys.map(|ty| {
let size = self.type_size_with_substs(ty, substs);
let align = self.type_align_with_substs(ty, substs);
// FIXME(solson)
self.memory.allocate(size, align).map(Value::ByRef)
}).collect();
// Subtract 1 because `local_decls` includes the ReturnPointer, but we don't store a local
// `Value` for that.
let num_locals = mir.local_decls.len() - 1;
let locals = vec![None; num_locals];
self.stack.push(Frame {
mir: mir.clone(),
block: mir::START_BLOCK,
return_to_block: return_to_block,
return_lvalue: return_lvalue,
locals: locals?,
locals: locals,
span: span,
def_id: def_id,
substs: substs,
@ -800,16 +792,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
use rustc::mir::repr::Operand::*;
match *op {
Consume(ref lvalue) => {
let val = match self.eval_lvalue(lvalue)? {
match self.eval_lvalue(lvalue)? {
Lvalue::Ptr { ptr, extra } => {
assert_eq!(extra, LvalueExtra::None);
Value::ByRef(ptr)
Ok(Value::ByRef(ptr))
}
Lvalue::Local { frame, local } => {
self.stack[frame].get_local(local)
self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes)
}
};
Ok(val)
}
}
Constant(mir::Constant { ref literal, ty, .. }) => {
@ -850,16 +841,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> {
fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> {
use rustc::mir::repr::Lvalue::*;
match *lvalue {
Local(i) if self.frame().mir.local_kind(i) == mir::LocalKind::ReturnPointer => {
Ok(self.frame().return_lvalue)
}
let lvalue = match *mir_lvalue {
Local(mir::RETURN_POINTER) => self.frame().return_lvalue,
Local(local) => {
let frame = self.stack.len() - 1;
Ok(Lvalue::Local { frame: frame, local: local })
Lvalue::Local {
frame: self.stack.len() - 1,
local: local,
}
}
Static(def_id) => {
@ -871,11 +862,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
};
let ptr = *self.statics.get(&cid)
.expect("static should have been cached (lvalue)");
Ok(Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None })
Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None }
}
Projection(ref proj) => self.eval_lvalue_projection(proj),
}
Projection(ref proj) => return self.eval_lvalue_projection(proj),
};
Ok(lvalue)
}
fn eval_lvalue_projection(
@ -1020,13 +1012,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let new_lvalue = match lvalue {
Lvalue::Local { frame, local } => {
let ptr = match self.stack[frame].get_local(local) {
Value::ByRef(ptr) => ptr,
val => {
Some(Value::ByRef(ptr)) => ptr,
opt_val => {
let ty = self.stack[frame].mir.local_decls[local].ty;
let substs = self.stack[frame].substs;
let ptr = self.alloc_ptr(ty, substs)?;
self.write_value_to_ptr(val, ptr, ty)?;
self.stack[frame].set_local(local, Value::ByRef(ptr));
if let Some(val) = opt_val {
self.write_value_to_ptr(val, ptr, ty)?;
}
ptr
}
};
@ -1133,7 +1127,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
Lvalue::Local { frame, local } => {
if let Value::ByRef(src_ptr) = value {
let dest_ptr = if let Value::ByRef(ptr) = self.stack[frame].get_local(local) {
let dest_val = self.stack[frame].get_local(local);
let dest_ptr = if let Some(Value::ByRef(ptr)) = dest_val {
ptr
} else {
let substs = self.substs();
@ -1367,14 +1362,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
impl<'a, 'tcx: 'a> Frame<'a, 'tcx> {
fn get_local(&self, local: mir::Local) -> Value {
fn get_local(&self, local: mir::Local) -> Option<Value> {
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
self.locals[local.index() - 1]
}
fn set_local(&mut self, local: mir::Local, value: Value) {
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
self.locals[local.index() - 1] = value;
self.locals[local.index() - 1] = Some(value);
}
}

View File

@ -216,15 +216,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let arg_locals = self.frame().mir.args_iter();
for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) {
// FIXME(solson)
let dest = self.frame().get_local(arg_local);
// FIXME(solson)
let dest = match dest {
Value::ByRef(p) => Lvalue::from_ptr(p),
_ => bug!("all locals should be ByRef until I finish refactoring"),
};
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
self.write_value(arg_val, dest, arg_ty)?;
}

View File

@ -52,9 +52,11 @@ impl Pointer {
pub fn offset(self, i: isize) -> Self {
Pointer { offset: (self.offset as isize + i) as usize, ..self }
}
pub fn points_to_zst(&self) -> bool {
self.alloc_id == ZST_ALLOC_ID
}
pub fn to_int<'tcx>(&self) -> EvalResult<'tcx, usize> {
match self.alloc_id {
NEVER_ALLOC_ID |
@ -62,18 +64,21 @@ impl Pointer {
_ => Err(EvalError::ReadPointerAsBytes),
}
}
pub fn from_int(i: usize) -> Self {
Pointer {
alloc_id: ZST_ALLOC_ID,
offset: i,
}
}
fn zst_ptr() -> Self {
Pointer {
alloc_id: ZST_ALLOC_ID,
offset: 0,
}
}
pub fn never_ptr() -> Self {
Pointer {
alloc_id: NEVER_ALLOC_ID,

View File

@ -1,11 +1,7 @@
#![feature(custom_attribute, attr_literals)]
#![miri(memory_size=0)]
fn bar() {
let x = 5;
assert_eq!(x, 6);
}
fn main() {
bar(); //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory
let _x = [42; 10];
//~^ERROR tried to allocate 40 more bytes, but only 0 bytes are free of the 0 byte memory
}

View File

@ -2,8 +2,8 @@
#![miri(memory_size=1000)]
fn bar(i: i32) {
if i < 1000 {
bar(i + 1) //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 1000 byte memory
if i < 1000 { //~ERROR tried to allocate 4 more bytes, but only 1 bytes are free of the 1000 byte memory
bar(i + 1)
//~^NOTE inside call to bar
//~|NOTE inside call to bar
//~|NOTE inside call to bar
@ -25,6 +25,12 @@ fn bar(i: i32) {
//~|NOTE inside call to bar
//~|NOTE inside call to bar
//~|NOTE inside call to bar
//~|NOTE inside call to bar
//~|NOTE inside call to bar
//~|NOTE inside call to bar
//~|NOTE inside call to bar
//~|NOTE inside call to bar
//~|NOTE inside call to bar
}
}