step.rs 9.5 KB
Newer Older
1 2 3 4
//! This module contains the `EvalContext` methods for executing a single step of the interpreter.
//!
//! The main entry point is the `step` method.

5 6
use std::cell::Ref;

7
use rustc::hir::def_id::DefId;
8
use rustc::hir;
9
use rustc::mir::visit::{Visitor, LvalueContext};
10
use rustc::mir;
S
Scott Olson 已提交
11
use rustc::ty::layout::Layout;
12 13 14
use rustc::ty::{subst, self};

use error::{EvalResult, EvalError};
15 16
use eval_context::{EvalContext, StackPopCleanup, MirRef};
use lvalue::{Global, GlobalId, Lvalue};
17
use syntax::codemap::Span;
O
Oliver Schneider 已提交
18

19
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
20 21 22 23 24 25 26 27 28
    pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx, ()> {
        self.steps_remaining = self.steps_remaining.saturating_sub(n);
        if self.steps_remaining > 0 {
            Ok(())
        } else {
            Err(EvalError::ExecutionTimeLimitReached)
        }
    }

29 30
    /// Returns true as long as there are more things to do.
    pub fn step(&mut self) -> EvalResult<'tcx, bool> {
31
        self.inc_step_counter_and_check_limit(1)?;
32
        if self.stack.is_empty() {
33
            return Ok(false);
34 35
        }

36
        let block = self.frame().block;
37
        let stmt_id = self.frame().stmt;
38
        let mir = self.mir();
39
        let basic_block = &mir.basic_blocks()[block];
O
Oliver Schneider 已提交
40

O
Oliver Schneider 已提交
41
        if let Some(stmt) = basic_block.statements.get(stmt_id) {
O
Oliver Schneider 已提交
42
            let mut new = Ok(0);
43
            ConstantExtractor {
44
                span: stmt.source_info.span,
45 46 47
                substs: self.substs(),
                def_id: self.frame().def_id,
                ecx: self,
48
                mir: Ref::clone(&mir),
O
Oliver Schneider 已提交
49
                new_constants: &mut new,
S
Scott Olson 已提交
50
            }.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id });
O
Oliver Schneider 已提交
51
            if new? == 0 {
52
                self.statement(stmt)?;
53
            }
54 55
            // if ConstantExtractor added new frames, we don't execute anything here
            // but await the next call to step
56
            return Ok(true);
O
Oliver Schneider 已提交
57 58
        }

59
        let terminator = basic_block.terminator();
O
Oliver Schneider 已提交
60
        let mut new = Ok(0);
61
        ConstantExtractor {
62
            span: terminator.source_info.span,
63 64 65
            substs: self.substs(),
            def_id: self.frame().def_id,
            ecx: self,
66
            mir: Ref::clone(&mir),
O
Oliver Schneider 已提交
67
            new_constants: &mut new,
S
Scott Olson 已提交
68
        }.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id });
O
Oliver Schneider 已提交
69
        if new? == 0 {
70
            self.terminator(terminator)?;
71
        }
72 73
        // if ConstantExtractor added new frames, we don't execute anything here
        // but await the next call to step
74
        Ok(true)
O
Oliver Schneider 已提交
75
    }
76 77 78

    fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> {
        trace!("{:?}", stmt);
S
Scott Olson 已提交
79

80
        use rustc::mir::StatementKind::*;
S
Scott Olson 已提交
81
        match stmt.kind {
82
            Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,
S
Scott Olson 已提交
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

            SetDiscriminant { ref lvalue, variant_index } => {
                let dest = self.eval_lvalue(lvalue)?;
                let dest_ty = self.lvalue_ty(lvalue);
                let dest_layout = self.type_layout(dest_ty)?;

                match *dest_layout {
                    Layout::General { discr, ref variants, .. } => {
                        let discr_size = discr.size().bytes();
                        let discr_offset = variants[variant_index].offsets[0].bytes();

                        // FIXME(solson)
                        let dest = self.force_allocation(dest)?;
                        let discr_dest = (dest.to_ptr()).offset(discr_offset);

O
Oliver Schneider 已提交
98
                        self.memory.write_uint(discr_dest, variant_index as u128, discr_size)?;
S
Scott Olson 已提交
99 100 101 102 103 104 105 106 107 108 109 110
                    }

                    Layout::RawNullablePointer { nndiscr, .. } => {
                        use value::PrimVal;
                        if variant_index as u64 != nndiscr {
                            self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
                        }
                    }

                    _ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout),
                }
            }
S
Scott Olson 已提交
111

112 113 114
            // Miri can safely ignore these. Only translation needs it.
            StorageLive(_) |
            StorageDead(_) => {}
S
Scott Olson 已提交
115 116 117 118

            // Defined to do nothing. These are added by optimization passes, to avoid changing the
            // size of MIR constantly.
            Nop => {}
S
Scott Olson 已提交
119 120
        }

121 122 123 124 125 126 127 128 129 130 131 132
        self.frame_mut().stmt += 1;
        Ok(())
    }

    fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx, ()> {
        trace!("{:?}", terminator.kind);
        self.eval_terminator(terminator)?;
        if !self.stack.is_empty() {
            trace!("// {:?}", self.frame().block);
        }
        Ok(())
    }
133
}
134

135
// WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
136
// this includes any method that might access the stack
137
// basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame`
138 139
// The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons
struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
140
    span: Span,
141
    ecx: &'a mut EvalContext<'b, 'tcx>,
142
    mir: MirRef<'tcx>,
143 144
    def_id: DefId,
    substs: &'tcx subst::Substs<'tcx>,
O
Oliver Schneider 已提交
145
    new_constants: &'a mut EvalResult<'tcx, u64>,
146 147
}

148
impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
149
    fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, immutable: bool) {
S
Scott Olson 已提交
150
        let cid = GlobalId { def_id, substs, promoted: None };
151
        if self.ecx.globals.contains_key(&cid) {
152 153
            return;
        }
O
Oliver Schneider 已提交
154
        self.try(|this| {
155
            let mir = this.ecx.load_mir(def_id)?;
156
            this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
157
            let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() {
158
                StackPopCleanup::Freeze
159 160 161
            } else {
                StackPopCleanup::None
            };
162
            this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup, Vec::new())
O
Oliver Schneider 已提交
163 164 165 166 167 168 169 170 171 172 173
        });
    }
    fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, ()>>(&mut self, f: F) {
        if let Ok(ref mut n) = *self.new_constants {
            *n += 1;
        } else {
            return;
        }
        if let Err(e) = f(self) {
            *self.new_constants = Err(e);
        }
174
    }
175 176
}

177
impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
178 179
    fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
        self.super_constant(constant, location);
180 181 182
        match constant.literal {
            // already computed by rustc
            mir::Literal::Value { .. } => {}
183
            mir::Literal::Item { def_id, substs } => {
184
                if let ty::TyFnDef(..) = constant.ty.sty {
185
                    // No need to do anything here,
186 187
                    // because the type is the actual function, not the signature of the function.
                    // Thus we can simply create a zero sized allocation in `evaluate_operand`
188
                } else {
189
                    self.global_item(def_id, substs, constant.span, true);
190 191
                }
            },
192
            mir::Literal::Promoted { index } => {
193
                let cid = GlobalId {
194 195
                    def_id: self.def_id,
                    substs: self.substs,
O
Oliver Schneider 已提交
196
                    promoted: Some(index),
197
                };
198
                if self.ecx.globals.contains_key(&cid) {
199 200
                    return;
                }
201 202
                let mir = Ref::clone(&self.mir);
                let mir = Ref::map(mir, |mir| &mir.promoted[index]);
O
Oliver Schneider 已提交
203
                self.try(|this| {
204
                    let ty = this.ecx.monomorphize(mir.return_ty, this.substs);
205
                    this.ecx.globals.insert(cid, Global::uninitialized(ty));
206 207 208 209
                    this.ecx.push_stack_frame(this.def_id,
                                              constant.span,
                                              mir,
                                              this.substs,
210
                                              Lvalue::Global(cid),
211 212
                                              StackPopCleanup::Freeze,
                                              Vec::new())
O
Oliver Schneider 已提交
213
                });
214 215 216 217
            }
        }
    }

S
Scott Olson 已提交
218 219 220 221 222 223
    fn visit_lvalue(
        &mut self,
        lvalue: &mir::Lvalue<'tcx>,
        context: LvalueContext<'tcx>,
        location: mir::Location
    ) {
224
        self.super_lvalue(lvalue, context, location);
225
        if let mir::Lvalue::Static(def_id) = *lvalue {
226
            let substs = self.ecx.tcx.intern_substs(&[]);
227
            let span = self.span;
228 229 230 231 232 233 234 235
            if let Some(node_item) = self.ecx.tcx.map.get_if_local(def_id) {
                if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item {
                    if let hir::ItemStatic(_, m, _) = *node {
                        self.global_item(def_id, substs, span, m == hir::MutImmutable);
                        return;
                    } else {
                        bug!("static def id doesn't point to static");
                    }
236
                } else {
237
                    bug!("static def id doesn't point to item");
238 239
                }
            } else {
240
                let def = self.ecx.tcx.sess.cstore.describe_def(def_id).expect("static not found");
241 242 243 244 245
                if let hir::def::Def::Static(_, mutable) = def {
                    self.global_item(def_id, substs, span, !mutable);
                } else {
                    bug!("static found but isn't a static: {:?}", def);
                }
246
            }
247 248
        }
    }
O
Oliver Schneider 已提交
249
}