Merge pull request #68 from oli-obk/master
priroda requirements + static impl
This commit is contained in:
commit
7f3cb7fdb8
5 changed files with 201 additions and 114 deletions
|
@ -15,9 +15,9 @@ use std::rc::Rc;
|
||||||
use syntax::codemap::{self, DUMMY_SP};
|
use syntax::codemap::{self, DUMMY_SP};
|
||||||
|
|
||||||
use error::{EvalError, EvalResult};
|
use error::{EvalError, EvalResult};
|
||||||
use memory::{Memory, Pointer, AllocId};
|
use memory::{Memory, Pointer};
|
||||||
use primval::{self, PrimVal, PrimValKind};
|
use primval::{self, PrimVal, PrimValKind};
|
||||||
use self::value::Value;
|
pub use self::value::Value;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
@ -41,8 +41,7 @@ pub struct EvalContext<'a, 'tcx: 'a> {
|
||||||
memory: Memory<'a, 'tcx>,
|
memory: Memory<'a, 'tcx>,
|
||||||
|
|
||||||
/// Precomputed statics, constants and promoteds.
|
/// Precomputed statics, constants and promoteds.
|
||||||
// FIXME(solson): Change from Pointer to Value.
|
globals: HashMap<GlobalId<'tcx>, Global<'tcx>>,
|
||||||
statics: HashMap<ConstantId<'tcx>, Pointer>,
|
|
||||||
|
|
||||||
/// The virtual call stack.
|
/// The virtual call stack.
|
||||||
stack: Vec<Frame<'a, 'tcx>>,
|
stack: Vec<Frame<'a, 'tcx>>,
|
||||||
|
@ -77,7 +76,7 @@ pub struct Frame<'a, 'tcx: 'a> {
|
||||||
pub return_to_block: StackPopCleanup,
|
pub return_to_block: StackPopCleanup,
|
||||||
|
|
||||||
/// The location where the result of the current stack frame should be written to.
|
/// The location where the result of the current stack frame should be written to.
|
||||||
pub return_lvalue: Lvalue,
|
pub return_lvalue: Lvalue<'tcx>,
|
||||||
|
|
||||||
/// The list of locals for this stack frame, stored in order as
|
/// The list of locals for this stack frame, stored in order as
|
||||||
/// `[arguments..., variables..., temporaries...]`. The locals are stored as `Value`s, which
|
/// `[arguments..., variables..., temporaries...]`. The locals are stored as `Value`s, which
|
||||||
|
@ -99,7 +98,7 @@ pub struct Frame<'a, 'tcx: 'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Lvalue {
|
pub enum Lvalue<'tcx> {
|
||||||
/// An lvalue referring to a value allocated in the `Memory` system.
|
/// An lvalue referring to a value allocated in the `Memory` system.
|
||||||
Ptr {
|
Ptr {
|
||||||
ptr: Pointer,
|
ptr: Pointer,
|
||||||
|
@ -111,9 +110,11 @@ pub enum Lvalue {
|
||||||
Local {
|
Local {
|
||||||
frame: usize,
|
frame: usize,
|
||||||
local: mir::Local,
|
local: mir::Local,
|
||||||
}
|
},
|
||||||
|
|
||||||
// TODO(solson): Static/Const? None/Never?
|
Global(GlobalId<'tcx>),
|
||||||
|
|
||||||
|
// TODO(solson): None/Never?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
@ -130,9 +131,9 @@ pub enum CachedMir<'mir, 'tcx: 'mir> {
|
||||||
Owned(Rc<mir::Mir<'tcx>>)
|
Owned(Rc<mir::Mir<'tcx>>)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
/// Uniquely identifies a specific constant or static
|
/// Uniquely identifies a specific constant or static
|
||||||
struct ConstantId<'tcx> {
|
pub struct GlobalId<'tcx> {
|
||||||
/// the def id of the constant/static or in case of promoteds, the def id of the function they belong to
|
/// the def id of the constant/static or in case of promoteds, the def id of the function they belong to
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
/// In case of statics and constants this is `Substs::empty()`, so only promoteds and associated
|
/// In case of statics and constants this is `Substs::empty()`, so only promoteds and associated
|
||||||
|
@ -140,21 +141,33 @@ struct ConstantId<'tcx> {
|
||||||
/// but that would only require more branching when working with constants, and not bring any
|
/// but that would only require more branching when working with constants, and not bring any
|
||||||
/// real benefits.
|
/// real benefits.
|
||||||
substs: &'tcx Substs<'tcx>,
|
substs: &'tcx Substs<'tcx>,
|
||||||
kind: ConstantKind,
|
/// this `Option` is `Some` for promoted constants
|
||||||
|
promoted: Option<mir::Promoted>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
enum ConstantKind {
|
pub struct Global<'tcx> {
|
||||||
Promoted(mir::Promoted),
|
data: Option<Value>,
|
||||||
/// Statics, constants and associated constants
|
mutable: bool,
|
||||||
Global,
|
ty: Ty<'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Global<'tcx> {
|
||||||
|
fn uninitialized(ty: Ty<'tcx>) -> Self {
|
||||||
|
Global {
|
||||||
|
data: None,
|
||||||
|
mutable: true,
|
||||||
|
ty: ty,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub enum StackPopCleanup {
|
pub enum StackPopCleanup {
|
||||||
/// The stackframe existed to compute the initial value of a static/constant, make sure the
|
/// The stackframe existed to compute the initial value of a static/constant, make sure it
|
||||||
/// static isn't modifyable afterwards
|
/// isn't modifyable afterwards. The allocation of the result is frozen iff it's an
|
||||||
Freeze(AllocId),
|
/// actual allocation. `PrimVal`s are unmodifyable anyway.
|
||||||
|
Freeze,
|
||||||
/// A regular stackframe added due to a function call will need to get forwarded to the next
|
/// A regular stackframe added due to a function call will need to get forwarded to the next
|
||||||
/// block
|
/// block
|
||||||
Goto(mir::BasicBlock),
|
Goto(mir::BasicBlock),
|
||||||
|
@ -169,7 +182,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
mir_map: mir_map,
|
mir_map: mir_map,
|
||||||
mir_cache: RefCell::new(DefIdMap()),
|
mir_cache: RefCell::new(DefIdMap()),
|
||||||
memory: Memory::new(&tcx.data_layout, memory_size),
|
memory: Memory::new(&tcx.data_layout, memory_size),
|
||||||
statics: HashMap::new(),
|
globals: HashMap::new(),
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
stack_limit: stack_limit,
|
stack_limit: stack_limit,
|
||||||
}
|
}
|
||||||
|
@ -347,7 +360,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
span: codemap::Span,
|
span: codemap::Span,
|
||||||
mir: CachedMir<'a, 'tcx>,
|
mir: CachedMir<'a, 'tcx>,
|
||||||
substs: &'tcx Substs<'tcx>,
|
substs: &'tcx Substs<'tcx>,
|
||||||
return_lvalue: Lvalue,
|
return_lvalue: Lvalue<'tcx>,
|
||||||
return_to_block: StackPopCleanup,
|
return_to_block: StackPopCleanup,
|
||||||
) -> EvalResult<'tcx, ()> {
|
) -> EvalResult<'tcx, ()> {
|
||||||
::log_settings::settings().indentation += 1;
|
::log_settings::settings().indentation += 1;
|
||||||
|
@ -380,7 +393,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
::log_settings::settings().indentation -= 1;
|
::log_settings::settings().indentation -= 1;
|
||||||
let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none");
|
let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none");
|
||||||
match frame.return_to_block {
|
match frame.return_to_block {
|
||||||
StackPopCleanup::Freeze(alloc_id) => self.memory.freeze(alloc_id)?,
|
StackPopCleanup::Freeze => if let Lvalue::Global(id) = frame.return_lvalue {
|
||||||
|
let global_value = self.globals
|
||||||
|
.get_mut(&id)
|
||||||
|
.expect("global should have been cached (freeze)");
|
||||||
|
if let Value::ByRef(ptr) = global_value.data.expect("global should have been initialized") {
|
||||||
|
self.memory.freeze(ptr.alloc_id)?;
|
||||||
|
}
|
||||||
|
assert!(global_value.mutable);
|
||||||
|
global_value.mutable = false;
|
||||||
|
} else {
|
||||||
|
bug!("StackPopCleanup::Freeze on: {:?}", frame.return_lvalue);
|
||||||
|
},
|
||||||
StackPopCleanup::Goto(target) => self.goto_block(target),
|
StackPopCleanup::Goto(target) => self.goto_block(target),
|
||||||
StackPopCleanup::None => {},
|
StackPopCleanup::None => {},
|
||||||
}
|
}
|
||||||
|
@ -406,7 +430,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
op: mir::BinOp,
|
op: mir::BinOp,
|
||||||
left: &mir::Operand<'tcx>,
|
left: &mir::Operand<'tcx>,
|
||||||
right: &mir::Operand<'tcx>,
|
right: &mir::Operand<'tcx>,
|
||||||
dest: Lvalue,
|
dest: Lvalue<'tcx>,
|
||||||
dest_ty: Ty<'tcx>,
|
dest_ty: Ty<'tcx>,
|
||||||
) -> EvalResult<'tcx, ()> {
|
) -> EvalResult<'tcx, ()> {
|
||||||
let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
|
let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
|
||||||
|
@ -421,7 +445,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
op: mir::BinOp,
|
op: mir::BinOp,
|
||||||
left: &mir::Operand<'tcx>,
|
left: &mir::Operand<'tcx>,
|
||||||
right: &mir::Operand<'tcx>,
|
right: &mir::Operand<'tcx>,
|
||||||
dest: Lvalue,
|
dest: Lvalue<'tcx>,
|
||||||
) -> EvalResult<'tcx, bool> {
|
) -> EvalResult<'tcx, bool> {
|
||||||
let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
|
let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
|
||||||
self.write_primval(dest, val)?;
|
self.write_primval(dest, val)?;
|
||||||
|
@ -430,7 +454,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
|
|
||||||
fn assign_fields<I: IntoIterator<Item = u64>>(
|
fn assign_fields<I: IntoIterator<Item = u64>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
dest: Lvalue,
|
dest: Lvalue<'tcx>,
|
||||||
offsets: I,
|
offsets: I,
|
||||||
operands: &[mir::Operand<'tcx>],
|
operands: &[mir::Operand<'tcx>],
|
||||||
) -> EvalResult<'tcx, ()> {
|
) -> EvalResult<'tcx, ()> {
|
||||||
|
@ -812,26 +836,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
// function items are zero sized
|
// function items are zero sized
|
||||||
Value::ByRef(self.memory.allocate(0, 0)?)
|
Value::ByRef(self.memory.allocate(0, 0)?)
|
||||||
} else {
|
} else {
|
||||||
let cid = ConstantId {
|
let cid = GlobalId {
|
||||||
def_id: def_id,
|
def_id: def_id,
|
||||||
substs: substs,
|
substs: substs,
|
||||||
kind: ConstantKind::Global,
|
promoted: None,
|
||||||
};
|
};
|
||||||
let static_ptr = *self.statics.get(&cid)
|
self.read_lvalue(Lvalue::Global(cid))?
|
||||||
.expect("static should have been cached (rvalue)");
|
|
||||||
Value::ByRef(static_ptr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Literal::Promoted { index } => {
|
Literal::Promoted { index } => {
|
||||||
let cid = ConstantId {
|
let cid = GlobalId {
|
||||||
def_id: self.frame().def_id,
|
def_id: self.frame().def_id,
|
||||||
substs: self.substs(),
|
substs: self.substs(),
|
||||||
kind: ConstantKind::Promoted(index),
|
promoted: Some(index),
|
||||||
};
|
};
|
||||||
let static_ptr = *self.statics.get(&cid)
|
self.read_lvalue(Lvalue::Global(cid))?
|
||||||
.expect("a promoted constant hasn't been precomputed");
|
|
||||||
Value::ByRef(static_ptr)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -851,8 +871,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let lvalue = self.eval_lvalue(lvalue)?;
|
||||||
|
self.read_lvalue(lvalue)
|
||||||
|
}
|
||||||
|
|
||||||
match self.eval_lvalue(lvalue)? {
|
pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
|
||||||
|
match lvalue {
|
||||||
Lvalue::Ptr { ptr, extra } => {
|
Lvalue::Ptr { ptr, extra } => {
|
||||||
assert_eq!(extra, LvalueExtra::None);
|
assert_eq!(extra, LvalueExtra::None);
|
||||||
Ok(Value::ByRef(ptr))
|
Ok(Value::ByRef(ptr))
|
||||||
|
@ -860,10 +884,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
Lvalue::Local { frame, local } => {
|
Lvalue::Local { frame, local } => {
|
||||||
self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes)
|
self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes)
|
||||||
}
|
}
|
||||||
|
Lvalue::Global(cid) => self.globals
|
||||||
|
.get(&cid)
|
||||||
|
.expect("global not cached")
|
||||||
|
.data
|
||||||
|
.ok_or(EvalError::ReadUndefBytes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> {
|
fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
|
||||||
use rustc::mir::repr::Lvalue::*;
|
use rustc::mir::repr::Lvalue::*;
|
||||||
let lvalue = match *mir_lvalue {
|
let lvalue = match *mir_lvalue {
|
||||||
Local(mir::RETURN_POINTER) => self.frame().return_lvalue,
|
Local(mir::RETURN_POINTER) => self.frame().return_lvalue,
|
||||||
|
@ -877,14 +906,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
|
|
||||||
Static(def_id) => {
|
Static(def_id) => {
|
||||||
let substs = subst::Substs::empty(self.tcx);
|
let substs = subst::Substs::empty(self.tcx);
|
||||||
let cid = ConstantId {
|
let cid = GlobalId {
|
||||||
def_id: def_id,
|
def_id: def_id,
|
||||||
substs: substs,
|
substs: substs,
|
||||||
kind: ConstantKind::Global,
|
promoted: None,
|
||||||
};
|
};
|
||||||
let ptr = *self.statics.get(&cid)
|
Lvalue::Global(cid)
|
||||||
.expect("static should have been cached (lvalue)");
|
|
||||||
Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Projection(ref proj) => return self.eval_lvalue_projection(proj),
|
Projection(ref proj) => return self.eval_lvalue_projection(proj),
|
||||||
|
@ -900,7 +927,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
fn eval_lvalue_projection(
|
fn eval_lvalue_projection(
|
||||||
&mut self,
|
&mut self,
|
||||||
proj: &mir::LvalueProjection<'tcx>,
|
proj: &mir::LvalueProjection<'tcx>,
|
||||||
) -> EvalResult<'tcx, Lvalue> {
|
) -> EvalResult<'tcx, Lvalue<'tcx>> {
|
||||||
let base = self.eval_lvalue(&proj.base)?;
|
let base = self.eval_lvalue(&proj.base)?;
|
||||||
let base_ty = self.lvalue_ty(&proj.base);
|
let base_ty = self.lvalue_ty(&proj.base);
|
||||||
let base_layout = self.type_layout(base_ty);
|
let base_layout = self.type_layout(base_ty);
|
||||||
|
@ -1069,11 +1096,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn force_allocation(&mut self, lvalue: Lvalue) -> EvalResult<'tcx, Lvalue> {
|
fn force_allocation(&mut self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
|
||||||
let new_lvalue = match lvalue {
|
let new_lvalue = match lvalue {
|
||||||
Lvalue::Local { frame, local } => {
|
Lvalue::Local { frame, local } => {
|
||||||
let ptr = match self.stack[frame].get_local(local) {
|
match self.stack[frame].get_local(local) {
|
||||||
Some(Value::ByRef(ptr)) => ptr,
|
Some(Value::ByRef(ptr)) => Lvalue::from_ptr(ptr),
|
||||||
opt_val => {
|
opt_val => {
|
||||||
let ty = self.stack[frame].mir.local_decls[local].ty;
|
let ty = self.stack[frame].mir.local_decls[local].ty;
|
||||||
let substs = self.stack[frame].substs;
|
let substs = self.stack[frame].substs;
|
||||||
|
@ -1082,12 +1109,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
if let Some(val) = opt_val {
|
if let Some(val) = opt_val {
|
||||||
self.write_value_to_ptr(val, ptr, ty)?;
|
self.write_value_to_ptr(val, ptr, ty)?;
|
||||||
}
|
}
|
||||||
ptr
|
Lvalue::from_ptr(ptr)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None }
|
|
||||||
}
|
}
|
||||||
Lvalue::Ptr { .. } => lvalue,
|
Lvalue::Ptr { .. } => lvalue,
|
||||||
|
Lvalue::Global(cid) => {
|
||||||
|
let global_val = *self.globals.get(&cid).expect("global not cached");
|
||||||
|
match global_val.data {
|
||||||
|
Some(Value::ByRef(ptr)) => Lvalue::from_ptr(ptr),
|
||||||
|
_ => {
|
||||||
|
let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.substs)?;
|
||||||
|
if let Some(val) = global_val.data {
|
||||||
|
self.write_value_to_ptr(val, ptr, global_val.ty)?;
|
||||||
|
}
|
||||||
|
if !global_val.mutable {
|
||||||
|
self.memory.freeze(ptr.alloc_id)?;
|
||||||
|
}
|
||||||
|
let lval = self.globals.get_mut(&cid).expect("already checked");
|
||||||
|
*lval = Global {
|
||||||
|
data: Some(Value::ByRef(ptr)),
|
||||||
|
.. global_val
|
||||||
|
};
|
||||||
|
Lvalue::from_ptr(ptr)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(new_lvalue)
|
Ok(new_lvalue)
|
||||||
}
|
}
|
||||||
|
@ -1136,7 +1183,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
|
|
||||||
fn write_primval(
|
fn write_primval(
|
||||||
&mut self,
|
&mut self,
|
||||||
dest: Lvalue,
|
dest: Lvalue<'tcx>,
|
||||||
val: PrimVal,
|
val: PrimVal,
|
||||||
) -> EvalResult<'tcx, ()> {
|
) -> EvalResult<'tcx, ()> {
|
||||||
match dest {
|
match dest {
|
||||||
|
@ -1148,58 +1195,94 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
self.stack[frame].set_local(local, Value::ByVal(val));
|
self.stack[frame].set_local(local, Value::ByVal(val));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Lvalue::Global(cid) => {
|
||||||
|
let global_val = self.globals.get_mut(&cid).expect("global not cached");
|
||||||
|
if global_val.mutable {
|
||||||
|
global_val.data = Some(Value::ByVal(val));
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(EvalError::ModifiedConstantMemory)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_value(
|
fn write_value(
|
||||||
&mut self,
|
&mut self,
|
||||||
src_val: Value,
|
src_val: Value,
|
||||||
dest: Lvalue,
|
dest: Lvalue<'tcx>,
|
||||||
dest_ty: Ty<'tcx>,
|
dest_ty: Ty<'tcx>,
|
||||||
) -> EvalResult<'tcx, ()> {
|
) -> EvalResult<'tcx, ()> {
|
||||||
match dest {
|
match dest {
|
||||||
|
Lvalue::Global(cid) => {
|
||||||
|
let dest = *self.globals.get_mut(&cid).expect("global should be cached");
|
||||||
|
if !dest.mutable {
|
||||||
|
return Err(EvalError::ModifiedConstantMemory);
|
||||||
|
}
|
||||||
|
self.write_value_possibly_by_val(
|
||||||
|
src_val,
|
||||||
|
|this, val| *this.globals.get_mut(&cid).expect("already checked") = Global { data: Some(val), ..dest },
|
||||||
|
dest.data,
|
||||||
|
dest_ty,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
Lvalue::Ptr { ptr, extra } => {
|
Lvalue::Ptr { ptr, extra } => {
|
||||||
assert_eq!(extra, LvalueExtra::None);
|
assert_eq!(extra, LvalueExtra::None);
|
||||||
self.write_value_to_ptr(src_val, ptr, dest_ty)?;
|
self.write_value_to_ptr(src_val, ptr, dest_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The cases here can be a bit subtle. Read carefully!
|
|
||||||
Lvalue::Local { frame, local } => {
|
Lvalue::Local { frame, local } => {
|
||||||
let dest_val = self.stack[frame].get_local(local);
|
let dest = self.stack[frame].get_local(local);
|
||||||
|
self.write_value_possibly_by_val(
|
||||||
if let Some(Value::ByRef(dest_ptr)) = dest_val {
|
src_val,
|
||||||
// If the local value is already `ByRef` (that is, backed by an `Allocation`),
|
|this, val| this.stack[frame].set_local(local, val),
|
||||||
// then we must write the new value into this allocation, because there may be
|
dest,
|
||||||
// other pointers into the allocation. These other pointers are logically
|
dest_ty,
|
||||||
// pointers into the local variable, and must be able to observe the change.
|
)
|
||||||
//
|
|
||||||
// Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we
|
|
||||||
// knew for certain that there were no outstanding pointers to this local.
|
|
||||||
self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?;
|
|
||||||
|
|
||||||
} else if let Value::ByRef(src_ptr) = src_val {
|
|
||||||
// If the local value is not `ByRef`, then we know there are no pointers to it
|
|
||||||
// and we can simply overwrite the `Value` in the locals array directly.
|
|
||||||
//
|
|
||||||
// In this specific case, where the source value is `ByRef`, we must duplicate
|
|
||||||
// the allocation, because this is a by-value operation. It would be incorrect
|
|
||||||
// if they referred to the same allocation, since then a change to one would
|
|
||||||
// implicitly change the other.
|
|
||||||
//
|
|
||||||
// TODO(solson): It would be valid to attempt reading a primitive value out of
|
|
||||||
// the source and writing that into the destination without making an
|
|
||||||
// allocation. This would be a pure optimization.
|
|
||||||
let dest_ptr = self.alloc_ptr(dest_ty)?;
|
|
||||||
self.copy(src_ptr, dest_ptr, dest_ty)?;
|
|
||||||
self.stack[frame].set_local(local, Value::ByRef(dest_ptr));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Finally, we have the simple case where neither source nor destination are
|
|
||||||
// `ByRef`. We may simply copy the source value over the the destintion local.
|
|
||||||
self.stack[frame].set_local(local, src_val);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The cases here can be a bit subtle. Read carefully!
|
||||||
|
fn write_value_possibly_by_val<F: FnOnce(&mut Self, Value)>(
|
||||||
|
&mut self,
|
||||||
|
src_val: Value,
|
||||||
|
write_dest: F,
|
||||||
|
old_dest_val: Option<Value>,
|
||||||
|
dest_ty: Ty<'tcx>,
|
||||||
|
) -> EvalResult<'tcx, ()> {
|
||||||
|
if let Some(Value::ByRef(dest_ptr)) = old_dest_val {
|
||||||
|
// If the value is already `ByRef` (that is, backed by an `Allocation`),
|
||||||
|
// then we must write the new value into this allocation, because there may be
|
||||||
|
// other pointers into the allocation. These other pointers are logically
|
||||||
|
// pointers into the local variable, and must be able to observe the change.
|
||||||
|
//
|
||||||
|
// Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we
|
||||||
|
// knew for certain that there were no outstanding pointers to this allocation.
|
||||||
|
self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?;
|
||||||
|
|
||||||
|
} else if let Value::ByRef(src_ptr) = src_val {
|
||||||
|
// If the value is not `ByRef`, then we know there are no pointers to it
|
||||||
|
// and we can simply overwrite the `Value` in the locals array directly.
|
||||||
|
//
|
||||||
|
// In this specific case, where the source value is `ByRef`, we must duplicate
|
||||||
|
// the allocation, because this is a by-value operation. It would be incorrect
|
||||||
|
// if they referred to the same allocation, since then a change to one would
|
||||||
|
// implicitly change the other.
|
||||||
|
//
|
||||||
|
// TODO(solson): It would be valid to attempt reading a primitive value out of
|
||||||
|
// the source and writing that into the destination without making an
|
||||||
|
// allocation. This would be a pure optimization.
|
||||||
|
let dest_ptr = self.alloc_ptr(dest_ty)?;
|
||||||
|
self.copy(src_ptr, dest_ptr, dest_ty)?;
|
||||||
|
write_dest(self, Value::ByRef(dest_ptr));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Finally, we have the simple case where neither source nor destination are
|
||||||
|
// `ByRef`. We may simply copy the source value over the the destintion.
|
||||||
|
write_dest(self, src_val);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1491,7 +1574,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_local(&self, lvalue: Lvalue) {
|
fn dump_local(&self, lvalue: Lvalue<'tcx>) {
|
||||||
if let Lvalue::Local { frame, local } = lvalue {
|
if let Lvalue::Local { frame, local } = lvalue {
|
||||||
if let Some(val) = self.stack[frame].get_local(local) {
|
if let Some(val) = self.stack[frame].get_local(local) {
|
||||||
match val {
|
match val {
|
||||||
|
@ -1512,7 +1595,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx: 'a> Frame<'a, 'tcx> {
|
impl<'a, 'tcx: 'a> Frame<'a, 'tcx> {
|
||||||
fn get_local(&self, local: mir::Local) -> Option<Value> {
|
pub 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.
|
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
|
||||||
self.locals[local.index() - 1]
|
self.locals[local.index() - 1]
|
||||||
}
|
}
|
||||||
|
@ -1523,8 +1606,8 @@ impl<'a, 'tcx: 'a> Frame<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lvalue {
|
impl<'tcx> Lvalue<'tcx> {
|
||||||
fn from_ptr(ptr: Pointer) -> Self {
|
pub fn from_ptr(ptr: Pointer) -> Self {
|
||||||
Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None }
|
Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1542,7 +1625,7 @@ impl Lvalue {
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elem_ty_and_len<'tcx>(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
|
fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
|
||||||
match ty.sty {
|
match ty.sty {
|
||||||
ty::TyArray(elem, n) => (elem, n as u64),
|
ty::TyArray(elem, n) => (elem, n as u64),
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
CachedMir,
|
CachedMir,
|
||||||
ConstantId,
|
GlobalId,
|
||||||
EvalContext,
|
EvalContext,
|
||||||
Lvalue,
|
Lvalue,
|
||||||
ConstantKind,
|
|
||||||
StackPopCleanup,
|
StackPopCleanup,
|
||||||
|
Global,
|
||||||
};
|
};
|
||||||
use error::EvalResult;
|
use error::EvalResult;
|
||||||
use rustc::mir::repr as mir;
|
use rustc::mir::repr as mir;
|
||||||
|
@ -118,25 +118,23 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
|
||||||
|
|
||||||
impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
|
impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
|
||||||
fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, immutable: bool) {
|
fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, immutable: bool) {
|
||||||
let cid = ConstantId {
|
let cid = GlobalId {
|
||||||
def_id: def_id,
|
def_id: def_id,
|
||||||
substs: substs,
|
substs: substs,
|
||||||
kind: ConstantKind::Global,
|
promoted: None,
|
||||||
};
|
};
|
||||||
if self.ecx.statics.contains_key(&cid) {
|
if self.ecx.globals.contains_key(&cid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.try(|this| {
|
self.try(|this| {
|
||||||
let mir = this.ecx.load_mir(def_id)?;
|
let mir = this.ecx.load_mir(def_id)?;
|
||||||
// FIXME(solson): Don't allocate a pointer unconditionally.
|
this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
|
||||||
let ptr = this.ecx.alloc_ptr_with_substs(mir.return_ty, substs)?;
|
|
||||||
this.ecx.statics.insert(cid.clone(), ptr);
|
|
||||||
let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() {
|
let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() {
|
||||||
StackPopCleanup::Freeze(ptr.alloc_id)
|
StackPopCleanup::Freeze
|
||||||
} else {
|
} else {
|
||||||
StackPopCleanup::None
|
StackPopCleanup::None
|
||||||
};
|
};
|
||||||
this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::from_ptr(ptr), cleanup)
|
this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, ()>>(&mut self, f: F) {
|
fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, ()>>(&mut self, f: F) {
|
||||||
|
@ -167,27 +165,25 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mir::Literal::Promoted { index } => {
|
mir::Literal::Promoted { index } => {
|
||||||
let cid = ConstantId {
|
let mir = self.mir.promoted[index].clone();
|
||||||
|
let cid = GlobalId {
|
||||||
def_id: self.def_id,
|
def_id: self.def_id,
|
||||||
substs: self.substs,
|
substs: self.substs,
|
||||||
kind: ConstantKind::Promoted(index),
|
promoted: Some(index),
|
||||||
};
|
};
|
||||||
if self.ecx.statics.contains_key(&cid) {
|
if self.ecx.globals.contains_key(&cid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mir = self.mir.promoted[index].clone();
|
|
||||||
let return_ty = mir.return_ty;
|
|
||||||
self.try(|this| {
|
self.try(|this| {
|
||||||
// FIXME(solson): Don't allocate a pointer unconditionally.
|
|
||||||
let return_ptr = this.ecx.alloc_ptr_with_substs(return_ty, cid.substs)?;
|
|
||||||
let mir = CachedMir::Owned(Rc::new(mir));
|
let mir = CachedMir::Owned(Rc::new(mir));
|
||||||
this.ecx.statics.insert(cid.clone(), return_ptr);
|
let ty = this.ecx.monomorphize(mir.return_ty, this.substs);
|
||||||
|
this.ecx.globals.insert(cid, Global::uninitialized(ty));
|
||||||
this.ecx.push_stack_frame(this.def_id,
|
this.ecx.push_stack_frame(this.def_id,
|
||||||
constant.span,
|
constant.span,
|
||||||
mir,
|
mir,
|
||||||
this.substs,
|
this.substs,
|
||||||
Lvalue::from_ptr(return_ptr),
|
Lvalue::Global(cid),
|
||||||
StackPopCleanup::Freeze(return_ptr.alloc_id))
|
StackPopCleanup::Freeze)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
substs: &'tcx Substs<'tcx>,
|
substs: &'tcx Substs<'tcx>,
|
||||||
args: &[mir::Operand<'tcx>],
|
args: &[mir::Operand<'tcx>],
|
||||||
dest: Lvalue,
|
dest: Lvalue<'tcx>,
|
||||||
dest_ty: Ty<'tcx>,
|
dest_ty: Ty<'tcx>,
|
||||||
dest_layout: &'tcx Layout,
|
dest_layout: &'tcx Layout,
|
||||||
) -> EvalResult<'tcx, ()> {
|
) -> EvalResult<'tcx, ()> {
|
||||||
|
|
|
@ -148,7 +148,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
substs: &'tcx Substs<'tcx>,
|
substs: &'tcx Substs<'tcx>,
|
||||||
fn_ty: &'tcx BareFnTy,
|
fn_ty: &'tcx BareFnTy,
|
||||||
destination: Option<(Lvalue, mir::BasicBlock)>,
|
destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>,
|
||||||
arg_operands: &[mir::Operand<'tcx>],
|
arg_operands: &[mir::Operand<'tcx>],
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> EvalResult<'tcx, ()> {
|
) -> EvalResult<'tcx, ()> {
|
||||||
|
@ -262,7 +262,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
&mut self,
|
&mut self,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
args: &[mir::Operand<'tcx>],
|
args: &[mir::Operand<'tcx>],
|
||||||
dest: Lvalue,
|
dest: Lvalue<'tcx>,
|
||||||
dest_size: usize,
|
dest_size: usize,
|
||||||
) -> EvalResult<'tcx, ()> {
|
) -> EvalResult<'tcx, ()> {
|
||||||
let name = self.tcx.item_name(def_id);
|
let name = self.tcx.item_name(def_id);
|
||||||
|
|
|
@ -36,6 +36,9 @@ pub use interpreter::{
|
||||||
eval_main,
|
eval_main,
|
||||||
run_mir_passes,
|
run_mir_passes,
|
||||||
StackPopCleanup,
|
StackPopCleanup,
|
||||||
|
Value,
|
||||||
|
Lvalue,
|
||||||
|
LvalueExtra,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use memory::{
|
pub use memory::{
|
||||||
|
@ -43,3 +46,8 @@ pub use memory::{
|
||||||
Pointer,
|
Pointer,
|
||||||
AllocId,
|
AllocId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use primval::{
|
||||||
|
PrimVal,
|
||||||
|
PrimValKind,
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue