Extract Terminator evaluation out of the main loop.

This commit is contained in:
Scott Olson 2016-03-14 21:18:39 -06:00
parent 7bac5963b9
commit 4a22283d8b

View File

@ -62,15 +62,37 @@ impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> {
}
}
struct Interpreter<'a, 'tcx: 'a> {
/// The results of the type checker, from rustc.
tcx: &'a TyCtxt<'tcx>,
/// A mapping from NodeIds to Mir, from rustc. Only contains MIR for crate-local items.
mir_map: &'a MirMap<'tcx>,
/// A local cache from DefIds to Mir for non-crate-local items.
mir_cache: RefCell<DefIdMap<Rc<mir::Mir<'tcx>>>>,
/// The virtual memory system.
memory: Memory,
/// The virtual call stack.
stack: Vec<Frame<'a, 'tcx>>,
/// Another stack containing the type substitutions for the current function invocation. Exists
/// separately from `stack` because it must contain the `Substs` for a function while
/// *creating* the `Frame` for that same function.
substs_stack: Vec<&'tcx Substs<'tcx>>,
}
/// A stack frame.
struct Frame<'a, 'tcx: 'a> {
/// The MIR for the function called on this frame.
mir: CachedMir<'a, 'tcx>,
/// The block in the MIR this frame will execute once a fn call returns back to this frame.
/// The block this frame will execute when a function call returns back to this frame.
next_block: mir::BasicBlock,
/// A pointer for writing the return value of the current call, if it's not a diverging call.
/// A pointer for writing the return value of the current call if it's not a diverging call.
return_ptr: Option<Pointer>,
/// The list of locals for the current function, stored in order as
@ -85,27 +107,16 @@ struct Frame<'a, 'tcx: 'a> {
temp_offset: usize,
}
impl<'a, 'tcx: 'a> Frame<'a, 'tcx> {
fn arg_ptr(&self, i: u32) -> Pointer {
self.locals[i as usize]
}
/// Represents the action to be taken in the main loop as a result of executing a terminator.
enum TerminatorTarget {
/// Make a local jump to the given block.
Block(mir::BasicBlock),
fn var_ptr(&self, i: u32) -> Pointer {
self.locals[self.var_offset + i as usize]
}
/// Start executing from the new current frame. (For function calls.)
Call,
fn temp_ptr(&self, i: u32) -> Pointer {
self.locals[self.temp_offset + i as usize]
}
}
struct Interpreter<'a, 'tcx: 'a> {
tcx: &'a TyCtxt<'tcx>,
mir_map: &'a MirMap<'tcx>,
mir_cache: RefCell<DefIdMap<Rc<mir::Mir<'tcx>>>>,
memory: Memory,
stack: Vec<Frame<'a, 'tcx>>,
substs_stack: Vec<&'tcx Substs<'tcx>>,
/// Stop executing the current frame and resume the previous frame.
Return,
}
impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> {
@ -195,74 +206,12 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> {
try!(self.eval_assignment(lvalue, rvalue));
}
if TRACE_EXECUTION { println!("{:?}", block_data.terminator()); }
use rustc::mir::repr::Terminator::*;
match *block_data.terminator() {
Return => break,
Goto { target } => current_block = target,
If { ref cond, targets: (then_target, else_target) } => {
let (cond_ptr, _) = try!(self.eval_operand(cond));
let cond_val = try!(self.memory.read_bool(cond_ptr));
current_block = if cond_val { then_target } else { else_target };
}
SwitchInt { ref discr, ref values, ref targets, .. } => {
let (discr_ptr, discr_repr) = try!(self.eval_lvalue(discr));
let discr_val = try!(self.memory.read_primval(discr_ptr, &discr_repr));
// Branch to the `otherwise` case by default, if no match is found.
current_block = targets[targets.len() - 1];
for (index, val_const) in values.iter().enumerate() {
let ptr = try!(self.const_to_ptr(val_const));
let val = try!(self.memory.read_primval(ptr, &discr_repr));
if discr_val == val {
current_block = targets[index];
break;
}
}
}
Switch { ref discr, ref targets, .. } => {
let (adt_ptr, adt_repr) = try!(self.eval_lvalue(discr));
let discr_repr = match adt_repr {
Repr::Sum { ref discr, .. } => discr,
_ => panic!("attmpted to switch on non-sum type"),
};
let discr_val = try!(self.memory.read_primval(adt_ptr, &discr_repr));
current_block = targets[discr_val.to_int() as usize];
}
Call { ref func, ref args, ref destination, .. } => {
let mut return_ptr = None;
if let Some((ref lv, target)) = *destination {
self.current_frame_mut().next_block = target;
return_ptr = Some(try!(self.eval_lvalue(lv)).0)
}
let func_ty = self.current_frame().mir.operand_ty(self.tcx, func);
match func_ty.sty {
ty::TyFnDef(def_id, substs, _) => {
let mir = self.load_mir(def_id);
self.substs_stack.push(substs);
try!(self.push_stack_frame(mir, args, return_ptr));
continue 'outer;
}
_ => panic!("can't handle callee of type {:?}", func_ty),
}
}
Drop { target, .. } => {
// TODO: Handle destructors and dynamic drop.
current_block = target;
}
Resume => unimplemented!(),
let terminator = block_data.terminator();
if TRACE_EXECUTION { println!("{:?}", terminator); }
match try!(self.eval_terminator(terminator)) {
TerminatorTarget::Block(block) => current_block = block,
TerminatorTarget::Return => break,
TerminatorTarget::Call => continue 'outer,
}
}
@ -273,6 +222,80 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> {
Ok(())
}
fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<TerminatorTarget> {
use rustc::mir::repr::Terminator::*;
let target = match *terminator {
Return => TerminatorTarget::Return,
Goto { target } => TerminatorTarget::Block(target),
If { ref cond, targets: (then_target, else_target) } => {
let (cond_ptr, _) = try!(self.eval_operand(cond));
let cond_val = try!(self.memory.read_bool(cond_ptr));
TerminatorTarget::Block(if cond_val { then_target } else { else_target })
}
SwitchInt { ref discr, ref values, ref targets, .. } => {
let (discr_ptr, discr_repr) = try!(self.eval_lvalue(discr));
let discr_val = try!(self.memory.read_primval(discr_ptr, &discr_repr));
// Branch to the `otherwise` case by default, if no match is found.
let mut target_block = targets[targets.len() - 1];
for (index, val_const) in values.iter().enumerate() {
let ptr = try!(self.const_to_ptr(val_const));
let val = try!(self.memory.read_primval(ptr, &discr_repr));
if discr_val == val {
target_block = targets[index];
break;
}
}
TerminatorTarget::Block(target_block)
}
Switch { ref discr, ref targets, .. } => {
let (adt_ptr, adt_repr) = try!(self.eval_lvalue(discr));
let discr_repr = match adt_repr {
Repr::Sum { ref discr, .. } => discr,
_ => panic!("attmpted to switch on non-sum type"),
};
let discr_val = try!(self.memory.read_primval(adt_ptr, &discr_repr));
TerminatorTarget::Block(targets[discr_val.to_int() as usize])
}
Call { ref func, ref args, ref destination, .. } => {
let mut return_ptr = None;
if let Some((ref lv, target)) = *destination {
self.current_frame_mut().next_block = target;
return_ptr = Some(try!(self.eval_lvalue(lv)).0)
}
let func_ty = self.current_frame().mir.operand_ty(self.tcx, func);
match func_ty.sty {
ty::TyFnDef(def_id, substs, _) => {
let mir = self.load_mir(def_id);
self.substs_stack.push(substs);
try!(self.push_stack_frame(mir, args, return_ptr));
TerminatorTarget::Call
}
_ => panic!("can't handle callee of type {:?}", func_ty),
}
}
Drop { target, .. } => {
// TODO: Handle destructors and dynamic drop.
TerminatorTarget::Block(target)
}
Resume => unimplemented!(),
};
Ok(target)
}
fn assign_to_product(&mut self, dest: Pointer, dest_repr: &Repr,
operands: &[mir::Operand<'tcx>]) -> EvalResult<()> {
match *dest_repr {
@ -521,6 +544,20 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> {
}
}
impl<'a, 'tcx: 'a> Frame<'a, 'tcx> {
fn arg_ptr(&self, i: u32) -> Pointer {
self.locals[i as usize]
}
fn var_ptr(&self, i: u32) -> Pointer {
self.locals[self.var_offset + i as usize]
}
fn temp_ptr(&self, i: u32) -> Pointer {
self.locals[self.temp_offset + i as usize]
}
}
pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) {
for (&id, mir) in &mir_map.map {
for attr in tcx.map.attrs(id) {