step.rs 10.0 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 value::{Value, PrimVal};
18
use syntax::codemap::Span;
O
Oliver Schneider 已提交
19

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

30 31
    /// Returns true as long as there are more things to do.
    pub fn step(&mut self) -> EvalResult<'tcx, bool> {
32
        // see docs on the `Memory::packed` field for why we do this
33
        self.memory.clear_packed();
34
        self.inc_step_counter_and_check_limit(1)?;
35
        if self.stack.is_empty() {
36
            return Ok(false);
37 38
        }

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

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

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

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

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

            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 {
91 92 93 94 95 96 97
                    Layout::General { discr, .. } => {
                        // FIXME: I (oli-obk) think we need to check the
                        // `dest_ty` for the variant's discriminant and write
                        // instead of the variant index
                        // We don't have any tests actually going through these lines
                        let discr_ty = discr.to_ty(&self.tcx, false);
                        let discr_lval = self.lvalue_field(dest, 0, dest_ty, discr_ty)?;
S
Scott Olson 已提交
98

99
                        self.write_value(Value::ByVal(PrimVal::Bytes(variant_index as u128)), discr_lval, discr_ty)?;
S
Scott Olson 已提交
100 101 102 103 104 105 106 107 108 109 110 111
                    }

                    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 已提交
112

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

            // Defined to do nothing. These are added by optimization passes, to avoid changing the
            // size of MIR constantly.
            Nop => {}
O
rustup  
Oliver Schneider 已提交
120 121

            InlineAsm { .. } => return Err(EvalError::InlineAsm),
S
Scott Olson 已提交
122 123
        }

124 125 126 127
        self.frame_mut().stmt += 1;
        Ok(())
    }

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

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

150
impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
S
Scott Olson 已提交
151 152 153 154 155 156 157
    fn global_item(
        &mut self,
        def_id: DefId,
        substs: &'tcx subst::Substs<'tcx>,
        span: Span,
        shared: bool,
    ) {
O
Oliver Schneider 已提交
158 159
        let instance = self.ecx.resolve_associated_const(def_id, substs);
        let cid = GlobalId { instance, promoted: None };
160
        if self.ecx.globals.contains_key(&cid) {
161 162
            return;
        }
O
Oliver Schneider 已提交
163
        self.try(|this| {
O
Oliver Schneider 已提交
164
            let mir = this.ecx.load_mir(instance.def)?;
165
            this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
S
Scott Olson 已提交
166 167
            let mutable = !shared || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe();
            let cleanup = StackPopCleanup::MarkStatic(mutable);
168 169
            let name = ty::tls::with(|tcx| tcx.item_path_str(def_id));
            trace!("pushing stack frame for global: {}", name);
S
Scott Olson 已提交
170
            this.ecx.push_stack_frame(
O
Oliver Schneider 已提交
171
                instance,
S
Scott Olson 已提交
172 173 174 175 176
                span,
                mir,
                Lvalue::Global(cid),
                cleanup,
            )
O
Oliver Schneider 已提交
177 178
        });
    }
179
    fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx>>(&mut self, f: F) {
O
Oliver Schneider 已提交
180 181 182 183 184 185 186 187
        if let Ok(ref mut n) = *self.new_constants {
            *n += 1;
        } else {
            return;
        }
        if let Err(e) = f(self) {
            *self.new_constants = Err(e);
        }
188
    }
189 190
}

191
impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
192 193
    fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
        self.super_constant(constant, location);
194 195 196
        match constant.literal {
            // already computed by rustc
            mir::Literal::Value { .. } => {}
197
            mir::Literal::Item { def_id, substs } => {
198
                if let ty::TyFnDef(..) = constant.ty.sty {
199
                    // No need to do anything here,
200 201
                    // 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`
202
                } else {
203
                    self.global_item(def_id, substs, constant.span, true);
204 205
                }
            },
206
            mir::Literal::Promoted { index } => {
207
                let cid = GlobalId {
O
Oliver Schneider 已提交
208
                    instance: self.instance,
O
Oliver Schneider 已提交
209
                    promoted: Some(index),
210
                };
211
                if self.ecx.globals.contains_key(&cid) {
212 213
                    return;
                }
214 215
                let mir = Ref::clone(&self.mir);
                let mir = Ref::map(mir, |mir| &mir.promoted[index]);
O
Oliver Schneider 已提交
216
                self.try(|this| {
O
Oliver Schneider 已提交
217
                    let ty = this.ecx.monomorphize(mir.return_ty, this.instance.substs);
218
                    this.ecx.globals.insert(cid, Global::uninitialized(ty));
219
                    trace!("pushing stack frame for {:?}", index);
O
Oliver Schneider 已提交
220
                    this.ecx.push_stack_frame(this.instance,
221 222
                                              constant.span,
                                              mir,
223
                                              Lvalue::Global(cid),
O
Oliver Schneider 已提交
224
                                              StackPopCleanup::MarkStatic(false))
O
Oliver Schneider 已提交
225
                });
226 227 228 229
            }
        }
    }

S
Scott Olson 已提交
230 231 232 233 234 235
    fn visit_lvalue(
        &mut self,
        lvalue: &mir::Lvalue<'tcx>,
        context: LvalueContext<'tcx>,
        location: mir::Location
    ) {
236
        self.super_lvalue(lvalue, context, location);
237 238
        if let mir::Lvalue::Static(ref static_) = *lvalue {
            let def_id = static_.def_id;
239
            let substs = self.ecx.tcx.intern_substs(&[]);
240
            let span = self.span;
A
Andrew Cann 已提交
241
            if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) {
242 243 244 245 246 247 248
                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");
                    }
249
                } else {
250
                    bug!("static def id doesn't point to item");
251 252
                }
            } else {
253
                let def = self.ecx.tcx.sess.cstore.describe_def(def_id).expect("static not found");
254 255 256 257 258
                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);
                }
259
            }
260 261
        }
    }
O
Oliver Schneider 已提交
262
}