mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-11 09:27:56 +00:00
jit interpretation of constants
This commit is contained in:
parent
05f829cc9f
commit
cc1ca73f57
@ -129,9 +129,9 @@ enum TerminatorTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
enum ConstantId {
|
enum ConstantId<'tcx> {
|
||||||
Promoted { index: usize },
|
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);
|
let mut nested_fecx = FnEvalContext::new(self);
|
||||||
|
|
||||||
nested_fecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, None);
|
nested_fecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, None);
|
||||||
let return_ptr = match mir.return_ty {
|
let return_ptr = nested_fecx.alloc_ret_ptr(mir.return_ty);
|
||||||
ty::FnConverging(ty) => {
|
|
||||||
let size = nested_fecx.type_size(ty);
|
|
||||||
Some(nested_fecx.memory.allocate(size))
|
|
||||||
}
|
|
||||||
ty::FnDiverging => None,
|
|
||||||
};
|
|
||||||
nested_fecx.frame_mut().return_ptr = return_ptr;
|
nested_fecx.frame_mut().return_ptr = return_ptr;
|
||||||
|
|
||||||
nested_fecx.run()?;
|
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<Pointer> {
|
||||||
|
match ty {
|
||||||
|
ty::FnConverging(ty) => {
|
||||||
|
let size = self.type_size(ty);
|
||||||
|
Some(self.memory.allocate(size))
|
||||||
|
}
|
||||||
|
ty::FnDiverging => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn maybe_report<T>(&self, span: codemap::Span, r: EvalResult<T>) -> EvalResult<T> {
|
fn maybe_report<T>(&self, span: codemap::Span, r: EvalResult<T>) -> EvalResult<T> {
|
||||||
if let Err(ref e) = r {
|
if let Err(ref e) = r {
|
||||||
let mut err = self.tcx.sess.struct_span_err(span, &e.to_string());
|
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 {
|
loop {
|
||||||
match stepper.step()? {
|
match stepper.step()? {
|
||||||
|
Constant => trace!("next statement requires the computation of a constant"),
|
||||||
Assignment => trace!("{:?}", stepper.stmt()),
|
Assignment => trace!("{:?}", stepper.stmt()),
|
||||||
Terminator => {
|
Terminator => {
|
||||||
trace!("{:?}", stepper.term().kind);
|
trace!("{:?}", stepper.term().kind);
|
||||||
@ -998,7 +1003,14 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||||||
use rustc::mir::repr::Literal::*;
|
use rustc::mir::repr::Literal::*;
|
||||||
match *literal {
|
match *literal {
|
||||||
Value { ref value } => Ok(self.const_to_ptr(value)?),
|
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")),
|
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],
|
Var(i) => self.frame().locals[self.frame().var_offset + i as usize],
|
||||||
Temp(i) => self.frame().locals[self.frame().temp_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) => {
|
Projection(ref proj) => {
|
||||||
let base = self.eval_lvalue(&proj.base)?;
|
let base = self.eval_lvalue(&proj.base)?;
|
||||||
|
@ -6,13 +6,15 @@ use super::{
|
|||||||
};
|
};
|
||||||
use error::EvalResult;
|
use error::EvalResult;
|
||||||
use rustc::mir::repr as mir;
|
use rustc::mir::repr as mir;
|
||||||
use rustc::ty::{self, subst};
|
use rustc::ty::subst::{self, Subst};
|
||||||
use rustc::mir::visit::Visitor;
|
use rustc::hir::def_id::DefId;
|
||||||
|
use rustc::mir::visit::{Visitor, LvalueContext};
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use memory::Pointer;
|
use memory::Pointer;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
Constant,
|
||||||
Assignment,
|
Assignment,
|
||||||
Terminator,
|
Terminator,
|
||||||
Done,
|
Done,
|
||||||
@ -26,21 +28,19 @@ pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
|
|||||||
mir: CachedMir<'mir, 'tcx>,
|
mir: CachedMir<'mir, 'tcx>,
|
||||||
process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>,
|
process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>,
|
||||||
// a stack of constants
|
// a stack of constants
|
||||||
constants: Vec<Vec<(ConstantId, Span)>>,
|
constants: Vec<Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, '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 {
|
pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self {
|
||||||
let mut stepper = Stepper {
|
Stepper {
|
||||||
block: fncx.frame().next_block,
|
block: fncx.frame().next_block,
|
||||||
mir: fncx.mir(),
|
mir: fncx.mir(),
|
||||||
fncx: fncx,
|
fncx: fncx,
|
||||||
stmt: vec![0],
|
stmt: vec![0],
|
||||||
process: Self::dummy,
|
process: Self::dummy,
|
||||||
constants: Vec::new(),
|
constants: vec![Vec::new()],
|
||||||
};
|
}
|
||||||
stepper.extract_constants();
|
|
||||||
stepper
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dummy(&mut self) -> EvalResult<()> { Ok(()) }
|
fn dummy(&mut self) -> EvalResult<()> { Ok(()) }
|
||||||
@ -81,20 +81,37 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx
|
|||||||
self.block = self.fncx.frame().next_block;
|
self.block = self.fncx.frame().next_block;
|
||||||
self.mir = self.fncx.mir();
|
self.mir = self.fncx.mir();
|
||||||
self.stmt.push(0);
|
self.stmt.push(0);
|
||||||
self.extract_constants();
|
self.constants.push(Vec::new());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc(&mut self, ty: ty::FnOutput<'tcx>) -> Pointer {
|
fn constant(&mut self) -> EvalResult<()> {
|
||||||
match ty {
|
match self.constants.last_mut().unwrap().pop() {
|
||||||
ty::FnConverging(ty) => {
|
Some((ConstantId::Promoted { index }, span, return_ptr, mir)) => {
|
||||||
let size = self.fncx.type_size(ty);
|
trace!("adding promoted constant {}, {:?}", index, span);
|
||||||
self.fncx.memory.allocate(size)
|
let substs = self.fncx.substs();
|
||||||
}
|
// FIXME: somehow encode that this is a promoted constant's frame
|
||||||
ty::FnDiverging => panic!("there's no such thing as an unreachable static"),
|
let def_id = self.fncx.frame().def_id;
|
||||||
|
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, substs }, span, return_ptr, mir)) => {
|
||||||
|
trace!("adding static {:?}, {:?}", def_id, span);
|
||||||
|
self.fncx.gecx.statics.insert(def_id, 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();
|
||||||
|
},
|
||||||
|
None => unreachable!(),
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self) -> EvalResult<Event> {
|
pub fn step(&mut self) -> EvalResult<Event> {
|
||||||
@ -106,45 +123,44 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx
|
|||||||
return Ok(Event::Done);
|
return Ok(Event::Done);
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.constants.last_mut().unwrap().pop() {
|
if !self.constants.last().unwrap().is_empty() {
|
||||||
Some((ConstantId::Promoted { index }, span)) => {
|
self.process = Self::constant;
|
||||||
trace!("adding promoted constant {}", index);
|
return Ok(Event::Constant);
|
||||||
let mir = self.mir.promoted[index].clone();
|
|
||||||
let return_ptr = self.alloc(mir.return_ty);
|
|
||||||
self.fncx.frame_mut().promoted.insert(index, return_ptr);
|
|
||||||
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.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);
|
|
||||||
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 => {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let basic_block = self.mir.basic_block_data(self.block);
|
let basic_block = self.mir.basic_block_data(self.block);
|
||||||
|
|
||||||
if basic_block.statements.len() > *self.stmt.last().unwrap() {
|
if let Some(ref stmt) = basic_block.statements.get(*self.stmt.last().unwrap()) {
|
||||||
self.process = Self::statement;
|
assert!(self.constants.last().unwrap().is_empty());
|
||||||
return Ok(Event::Assignment);
|
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;
|
let terminator = basic_block.terminator();
|
||||||
Ok(Event::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
|
/// 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);
|
let block_data = self.mir.basic_block_data(self.block);
|
||||||
block_data.terminator()
|
block_data.terminator()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_constants(&mut self) {
|
struct ConstantExtractor<'a: 'c, 'b: 'a + 'mir + 'c, 'c, 'mir: 'c, 'tcx: 'a + 'b + 'c> {
|
||||||
let mut extractor = ConstantExtractor {
|
constants: &'c mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
|
||||||
constants: Vec::new(),
|
span: Span,
|
||||||
|
mir: &'c mir::Mir<'tcx>,
|
||||||
|
fncx: &'c mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
};
|
};
|
||||||
extractor.visit_mir(&self.mir);
|
let mir = self.fncx.load_mir(def_id);
|
||||||
self.constants.push(extractor.constants);
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConstantExtractor {
|
impl<'a, 'b, 'c, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> {
|
||||||
constants: Vec<(ConstantId, Span)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for ConstantExtractor {
|
|
||||||
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
|
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
|
||||||
self.super_constant(constant);
|
self.super_constant(constant);
|
||||||
match constant.literal {
|
match constant.literal {
|
||||||
// already computed by rustc
|
// already computed by rustc
|
||||||
mir::Literal::Value { .. } => {}
|
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 } => {
|
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>) {
|
fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) {
|
||||||
self.super_statement(block, stmt);
|
self.super_lvalue(lvalue, context);
|
||||||
if let mir::StatementKind::Assign(mir::Lvalue::Static(def_id), _) = stmt.kind {
|
if let mir::Lvalue::Static(def_id) = *lvalue {
|
||||||
self.constants.push((ConstantId::Static { def_id: def_id }, stmt.span));
|
let substs = self.fncx.tcx.mk_substs(subst::Substs::empty());
|
||||||
|
let span = self.span;
|
||||||
|
self.constant(def_id, substs, span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![feature(custom_attribute)]
|
#![feature(custom_attribute)]
|
||||||
#![allow(dead_code, unused_attributes)]
|
#![allow(dead_code, unused_attributes)]
|
||||||
|
|
||||||
//error-pattern:static should have been cached
|
//error-pattern:unimplemented: mentions of function items
|
||||||
|
|
||||||
|
|
||||||
#[miri_run]
|
#[miri_run]
|
||||||
|
11
tests/run-pass/constants.rs
Normal file
11
tests/run-pass/constants.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#![feature(custom_attribute)]
|
||||||
|
#![allow(dead_code, unused_attributes)]
|
||||||
|
|
||||||
|
const A: usize = *&5;
|
||||||
|
|
||||||
|
#[miri_run]
|
||||||
|
fn foo() -> usize {
|
||||||
|
A
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
Reference in New Issue
Block a user