From cc1ca73f5736a4aaa7a1ae164788051b8366fa3d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 3 Jun 2016 15:48:56 +0200 Subject: [PATCH] jit interpretation of constants --- src/interpreter/mod.rs | 34 ++++-- src/interpreter/stepper.rs | 165 ++++++++++++++++++---------- tests/compile-fail/unimplemented.rs | 2 +- tests/run-pass/constants.rs | 11 ++ 4 files changed, 139 insertions(+), 73 deletions(-) create mode 100644 tests/run-pass/constants.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7ae20f78dc..7e59b2674e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -129,9 +129,9 @@ enum TerminatorTarget { } #[derive(Clone, Debug, Eq, PartialEq, Hash)] -enum ConstantId { +enum ConstantId<'tcx> { Promoted { index: usize }, - Static { def_id: DefId }, + Static { def_id: DefId, substs: &'tcx Substs<'tcx> }, } @@ -156,13 +156,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { let mut nested_fecx = FnEvalContext::new(self); nested_fecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, None); - let return_ptr = match mir.return_ty { - ty::FnConverging(ty) => { - let size = nested_fecx.type_size(ty); - Some(nested_fecx.memory.allocate(size)) - } - ty::FnDiverging => None, - }; + let return_ptr = nested_fecx.alloc_ret_ptr(mir.return_ty); nested_fecx.frame_mut().return_ptr = return_ptr; nested_fecx.run()?; @@ -178,6 +172,16 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } + fn alloc_ret_ptr(&mut self, ty: ty::FnOutput<'tcx>) -> Option { + match ty { + ty::FnConverging(ty) => { + let size = self.type_size(ty); + Some(self.memory.allocate(size)) + } + ty::FnDiverging => None, + } + } + fn maybe_report(&self, span: codemap::Span, r: EvalResult) -> EvalResult { if let Err(ref e) = r { let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); @@ -207,6 +211,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { loop { match stepper.step()? { + Constant => trace!("next statement requires the computation of a constant"), Assignment => trace!("{:?}", stepper.stmt()), Terminator => { trace!("{:?}", stepper.term().kind); @@ -998,7 +1003,14 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { use rustc::mir::repr::Literal::*; match *literal { Value { ref value } => Ok(self.const_to_ptr(value)?), - Item { .. } => Err(EvalError::Unimplemented(format!("literal items (e.g. mentions of function items) are unimplemented"))), + Item { def_id, substs } => { + let item_ty = self.tcx.lookup_item_type(def_id).subst(self.tcx, substs); + if item_ty.ty.is_fn() { + Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string())) + } else { + Ok(*self.statics.get(&def_id).expect("static should have been cached (rvalue)")) + } + }, Promoted { index } => Ok(*self.frame().promoted.get(&index).expect("a promoted constant hasn't been precomputed")), } } @@ -1014,7 +1026,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Var(i) => self.frame().locals[self.frame().var_offset + i as usize], Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize], - Static(def_id) => *self.gecx.statics.get(&def_id).expect("static should have been cached"), + Static(def_id) => *self.gecx.statics.get(&def_id).expect("static should have been cached (lvalue)"), Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 48773d6e0d..b84680f733 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -6,13 +6,15 @@ use super::{ }; use error::EvalResult; use rustc::mir::repr as mir; -use rustc::ty::{self, subst}; -use rustc::mir::visit::Visitor; +use rustc::ty::subst::{self, Subst}; +use rustc::hir::def_id::DefId; +use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; use memory::Pointer; use std::rc::Rc; pub enum Event { + Constant, Assignment, Terminator, Done, @@ -26,21 +28,19 @@ pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ mir: CachedMir<'mir, 'tcx>, process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>, // a stack of constants - constants: Vec>, + constants: Vec, Span, Pointer, CachedMir<'mir, 'tcx>)>>, } impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> { pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self { - let mut stepper = Stepper { + Stepper { block: fncx.frame().next_block, mir: fncx.mir(), fncx: fncx, stmt: vec![0], process: Self::dummy, - constants: Vec::new(), - }; - stepper.extract_constants(); - stepper + constants: vec![Vec::new()], + } } fn dummy(&mut self) -> EvalResult<()> { Ok(()) } @@ -81,70 +81,86 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); self.stmt.push(0); - self.extract_constants(); + self.constants.push(Vec::new()); }, } Ok(()) } - fn alloc(&mut self, ty: ty::FnOutput<'tcx>) -> Pointer { - match ty { - ty::FnConverging(ty) => { - let size = self.fncx.type_size(ty); - self.fncx.memory.allocate(size) - } - ty::FnDiverging => panic!("there's no such thing as an unreachable static"), - } - } - - pub fn step(&mut self) -> EvalResult { - (self.process)(self)?; - - if self.fncx.stack.is_empty() { - // fuse the iterator - self.process = Self::dummy; - return Ok(Event::Done); - } - + fn constant(&mut self) -> EvalResult<()> { match self.constants.last_mut().unwrap().pop() { - Some((ConstantId::Promoted { index }, span)) => { - trace!("adding promoted constant {}", index); - let mir = self.mir.promoted[index].clone(); - let return_ptr = self.alloc(mir.return_ty); - self.fncx.frame_mut().promoted.insert(index, return_ptr); + Some((ConstantId::Promoted { index }, span, return_ptr, mir)) => { + trace!("adding promoted constant {}, {:?}", index, span); let substs = self.fncx.substs(); // FIXME: somehow encode that this is a promoted constant's frame let def_id = self.fncx.frame().def_id; - self.fncx.push_stack_frame(def_id, span, CachedMir::Owned(Rc::new(mir)), substs, Some(return_ptr)); + self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); self.stmt.push(0); self.constants.push(Vec::new()); self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); }, - Some((ConstantId::Static { def_id }, span)) => { - trace!("adding static {:?}", def_id); - let mir = self.fncx.load_mir(def_id); - let return_ptr = self.alloc(mir.return_ty); + Some((ConstantId::Static { def_id, substs }, span, return_ptr, mir)) => { + trace!("adding static {:?}, {:?}", def_id, span); self.fncx.gecx.statics.insert(def_id, return_ptr); - let substs = self.fncx.tcx.mk_substs(subst::Substs::empty()); self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); self.stmt.push(0); self.constants.push(Vec::new()); self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); }, - None => {}, + None => unreachable!(), + } + Ok(()) + } + + pub fn step(&mut self) -> EvalResult { + (self.process)(self)?; + + if self.fncx.stack.is_empty() { + // fuse the iterator + self.process = Self::dummy; + return Ok(Event::Done); + } + + if !self.constants.last().unwrap().is_empty() { + self.process = Self::constant; + return Ok(Event::Constant); } let basic_block = self.mir.basic_block_data(self.block); - if basic_block.statements.len() > *self.stmt.last().unwrap() { - self.process = Self::statement; - return Ok(Event::Assignment); + if let Some(ref stmt) = basic_block.statements.get(*self.stmt.last().unwrap()) { + assert!(self.constants.last().unwrap().is_empty()); + ConstantExtractor { + constants: &mut self.constants.last_mut().unwrap(), + span: stmt.span, + fncx: self.fncx, + mir: &self.mir, + }.visit_statement(self.block, stmt); + if self.constants.last().unwrap().is_empty() { + self.process = Self::statement; + return Ok(Event::Assignment); + } else { + self.process = Self::constant; + return Ok(Event::Constant); + } } - self.process = Self::terminator; - Ok(Event::Terminator) + let terminator = basic_block.terminator(); + ConstantExtractor { + constants: &mut self.constants.last_mut().unwrap(), + span: terminator.span, + fncx: self.fncx, + mir: &self.mir, + }.visit_terminator(self.block, terminator); + if self.constants.last().unwrap().is_empty() { + self.process = Self::terminator; + Ok(Event::Terminator) + } else { + self.process = Self::constant; + return Ok(Event::Constant); + } } /// returns the basic block index of the currently processed block @@ -163,37 +179,64 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx let block_data = self.mir.basic_block_data(self.block); block_data.terminator() } +} - fn extract_constants(&mut self) { - let mut extractor = ConstantExtractor { - constants: Vec::new(), - }; - extractor.visit_mir(&self.mir); - self.constants.push(extractor.constants); - } +struct ConstantExtractor<'a: 'c, 'b: 'a + 'mir + 'c, 'c, 'mir: 'c, 'tcx: 'a + 'b + 'c> { + constants: &'c mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>, + span: Span, + mir: &'c mir::Mir<'tcx>, + fncx: &'c mut FnEvalContext<'a, 'b, 'mir, 'tcx>, } -struct ConstantExtractor { - constants: Vec<(ConstantId, Span)>, +impl<'a, 'b, 'c, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> { + fn constant(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { + if self.fncx.gecx.statics.contains_key(&def_id) { + return; + } + let cid = ConstantId::Static { + def_id: def_id, + substs: substs, + }; + let mir = self.fncx.load_mir(def_id); + let ptr = self.fncx.alloc_ret_ptr(mir.return_ty).expect("there's no such thing as an unreachable static"); + self.constants.push((cid, span, ptr, mir)); + } } -impl<'tcx> Visitor<'tcx> for ConstantExtractor { +impl<'a, 'b, 'c, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> { fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) { self.super_constant(constant); match constant.literal { // already computed by rustc mir::Literal::Value { .. } => {} - mir::Literal::Item { .. } => {}, // FIXME: unimplemented + mir::Literal::Item { def_id, substs } => { + let item_ty = self.fncx.tcx.lookup_item_type(def_id).subst(self.fncx.tcx, substs); + if item_ty.ty.is_fn() { + // unimplemented + } else { + self.constant(def_id, substs, constant.span); + } + }, mir::Literal::Promoted { index } => { - self.constants.push((ConstantId::Promoted { index: index }, constant.span)); + if self.fncx.frame().promoted.contains_key(&index) { + return; + } + let mir = self.mir.promoted[index].clone(); + let return_ty = mir.return_ty; + let return_ptr = self.fncx.alloc_ret_ptr(return_ty).expect("there's no such thing as an unreachable static"); + self.fncx.frame_mut().promoted.insert(index, return_ptr); + let mir = CachedMir::Owned(Rc::new(mir)); + self.constants.push((ConstantId::Promoted { index: index }, constant.span, return_ptr, mir)); } } } - fn visit_statement(&mut self, block: mir::BasicBlock, stmt: &mir::Statement<'tcx>) { - self.super_statement(block, stmt); - if let mir::StatementKind::Assign(mir::Lvalue::Static(def_id), _) = stmt.kind { - self.constants.push((ConstantId::Static { def_id: def_id }, stmt.span)); + fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) { + self.super_lvalue(lvalue, context); + if let mir::Lvalue::Static(def_id) = *lvalue { + let substs = self.fncx.tcx.mk_substs(subst::Substs::empty()); + let span = self.span; + self.constant(def_id, substs, span); } } } diff --git a/tests/compile-fail/unimplemented.rs b/tests/compile-fail/unimplemented.rs index 7ff4b1c191..754d3d9ee7 100644 --- a/tests/compile-fail/unimplemented.rs +++ b/tests/compile-fail/unimplemented.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -//error-pattern:static should have been cached +//error-pattern:unimplemented: mentions of function items #[miri_run] diff --git a/tests/run-pass/constants.rs b/tests/run-pass/constants.rs new file mode 100644 index 0000000000..3d6b08c340 --- /dev/null +++ b/tests/run-pass/constants.rs @@ -0,0 +1,11 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +const A: usize = *&5; + +#[miri_run] +fn foo() -> usize { + A +} + +fn main() {}