step.rs 11.6 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
use rustc::hir::def_id::DefId;
6
use rustc::hir;
7
use rustc::mir::visit::{Visitor, LvalueContext};
8
use rustc::mir;
S
Scott Olson 已提交
9
use rustc::ty::layout::Layout;
10 11 12
use rustc::ty::{subst, self};

use error::{EvalResult, EvalError};
13
use eval_context::{EvalContext, StackPopCleanup};
14
use lvalue::{Global, GlobalId, Lvalue};
15
use value::{Value, PrimVal};
16
use memory::Pointer;
17
use syntax::codemap::Span;
O
Oliver Schneider 已提交
18

19
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
20
    pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
21 22 23 24 25 26 27 28
        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
        // see docs on the `Memory::packed` field for why we do this
32
        self.memory.clear_packed();
33
        self.inc_step_counter_and_check_limit(1)?;
34
        if self.stack.is_empty() {
R
Ralf Jung 已提交
35 36
            if let Some((instance, ptr)) = self.memory.fetch_tls_dtor() {
                trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
37
                // TODO: Potientially, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs
R
Ralf Jung 已提交
38 39 40
                let mir = self.load_mir(instance.def)?;
                self.push_stack_frame(
                    instance,
41
                    mir.span,
R
Ralf Jung 已提交
42 43 44 45
                    mir,
                    Lvalue::from_ptr(Pointer::zst_ptr()),
                    StackPopCleanup::None,
                )?;
46 47 48 49
                let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?;
                let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
                let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
                self.write_value(Value::ByVal(PrimVal::Ptr(ptr)), dest, ty)?;
R
Ralf Jung 已提交
50 51
                return Ok(true);
            }
52
            return Ok(false);
53 54
        }

55
        let block = self.frame().block;
56
        let stmt_id = self.frame().stmt;
57
        let mir = self.mir();
58
        let basic_block = &mir.basic_blocks()[block];
O
Oliver Schneider 已提交
59

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

77
        let terminator = basic_block.terminator();
O
Oliver Schneider 已提交
78
        let mut new = Ok(0);
79
        ConstantExtractor {
80
            span: terminator.source_info.span,
O
Oliver Schneider 已提交
81
            instance: self.frame().instance,
82
            ecx: self,
83
            mir,
O
Oliver Schneider 已提交
84
            new_constants: &mut new,
S
Scott Olson 已提交
85
        }.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id });
R
Ralf Jung 已提交
86 87
        // if ConstantExtractor added new frames, we don't execute anything here
        // but await the next call to step
O
Oliver Schneider 已提交
88
        if new? == 0 {
89
            self.terminator(terminator)?;
90
        }
91
        Ok(true)
O
Oliver Schneider 已提交
92
    }
93

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

97
        use rustc::mir::StatementKind::*;
S
Scott Olson 已提交
98
        match stmt.kind {
99
            Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,
S
Scott Olson 已提交
100 101 102 103 104 105 106

            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 {
107 108 109 110 111 112 113
                    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 已提交
114

115
                        self.write_value(Value::ByVal(PrimVal::Bytes(variant_index as u128)), discr_lval, discr_ty)?;
S
Scott Olson 已提交
116 117 118 119 120 121 122 123 124 125 126 127
                    }

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

129 130 131 132 133 134 135 136 137 138 139 140 141 142
            // Mark locals as dead or alive.
            StorageLive(ref lvalue) | StorageDead(ref lvalue)=> {
                let (frame, local) = match self.eval_lvalue(lvalue)? {
                    Lvalue::Local{ frame, local, field: None } if self.stack.len() == frame+1 => (frame, local),
                    _ => return Err(EvalError::Unimplemented("Stroage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type
                };
                match stmt.kind {
                    StorageLive(_) => self.stack[frame].storage_live(local)?,
                    _ =>  {
                        let old_val = self.stack[frame].storage_dead(local)?;
                        self.deallocate_local(old_val)?;
                    }
                };
            }
S
Scott Olson 已提交
143 144 145 146

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

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

151 152 153 154
        self.frame_mut().stmt += 1;
        Ok(())
    }

155
    fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> {
156 157 158 159 160 161 162
        trace!("{:?}", terminator.kind);
        self.eval_terminator(terminator)?;
        if !self.stack.is_empty() {
            trace!("// {:?}", self.frame().block);
        }
        Ok(())
    }
163
}
164

165
// WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
166
// this includes any method that might access the stack
167
// basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame`
168 169
// The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons
struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
170
    span: Span,
171
    ecx: &'a mut EvalContext<'b, 'tcx>,
172
    mir: &'tcx mir::Mir<'tcx>,
O
Oliver Schneider 已提交
173
    instance: ty::Instance<'tcx>,
O
Oliver Schneider 已提交
174
    new_constants: &'a mut EvalResult<'tcx, u64>,
175 176
}

177
impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
S
Scott Olson 已提交
178 179 180 181 182 183 184
    fn global_item(
        &mut self,
        def_id: DefId,
        substs: &'tcx subst::Substs<'tcx>,
        span: Span,
        shared: bool,
    ) {
O
Oliver Schneider 已提交
185 186
        let instance = self.ecx.resolve_associated_const(def_id, substs);
        let cid = GlobalId { instance, promoted: None };
187
        if self.ecx.globals.contains_key(&cid) {
188 189
            return;
        }
190 191 192 193 194
        if self.ecx.tcx.has_attr(def_id, "linkage") {
            trace!("Initializing an extern global with NULL");
            self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Ptr(Pointer::from_int(0))), !shared));
            return;
        }
O
Oliver Schneider 已提交
195
        self.try(|this| {
O
Oliver Schneider 已提交
196
            let mir = this.ecx.load_mir(instance.def)?;
197
            this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
198 199 200
            let mutable = !shared ||
                !mir.return_ty.is_freeze(
                    this.ecx.tcx,
201
                    ty::ParamEnv::empty(),
202
                    span);
S
Scott Olson 已提交
203
            let cleanup = StackPopCleanup::MarkStatic(mutable);
204 205
            let name = ty::tls::with(|tcx| tcx.item_path_str(def_id));
            trace!("pushing stack frame for global: {}", name);
S
Scott Olson 已提交
206
            this.ecx.push_stack_frame(
O
Oliver Schneider 已提交
207
                instance,
S
Scott Olson 已提交
208 209 210 211 212
                span,
                mir,
                Lvalue::Global(cid),
                cleanup,
            )
O
Oliver Schneider 已提交
213 214
        });
    }
215

216
    fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx>>(&mut self, f: F) {
O
Oliver Schneider 已提交
217 218 219 220 221 222 223 224
        if let Ok(ref mut n) = *self.new_constants {
            *n += 1;
        } else {
            return;
        }
        if let Err(e) = f(self) {
            *self.new_constants = Err(e);
        }
225
    }
226 227
}

228
impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
229 230
    fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
        self.super_constant(constant, location);
231 232 233
        match constant.literal {
            // already computed by rustc
            mir::Literal::Value { .. } => {}
234
            mir::Literal::Item { def_id, substs } => {
235
                self.global_item(def_id, substs, constant.span, true);
236
            },
237
            mir::Literal::Promoted { index } => {
238
                let cid = GlobalId {
O
Oliver Schneider 已提交
239
                    instance: self.instance,
O
Oliver Schneider 已提交
240
                    promoted: Some(index),
241
                };
242
                if self.ecx.globals.contains_key(&cid) {
243 244
                    return;
                }
245
                let mir = &self.mir.promoted[index];
O
Oliver Schneider 已提交
246
                self.try(|this| {
O
Oliver Schneider 已提交
247
                    let ty = this.ecx.monomorphize(mir.return_ty, this.instance.substs);
248
                    this.ecx.globals.insert(cid, Global::uninitialized(ty));
249
                    trace!("pushing stack frame for {:?}", index);
O
Oliver Schneider 已提交
250
                    this.ecx.push_stack_frame(this.instance,
251 252
                                              constant.span,
                                              mir,
253
                                              Lvalue::Global(cid),
254 255
                                              StackPopCleanup::MarkStatic(false),
                    )
O
Oliver Schneider 已提交
256
                });
257 258 259 260
            }
        }
    }

S
Scott Olson 已提交
261 262 263 264 265 266
    fn visit_lvalue(
        &mut self,
        lvalue: &mir::Lvalue<'tcx>,
        context: LvalueContext<'tcx>,
        location: mir::Location
    ) {
267
        self.super_lvalue(lvalue, context, location);
268 269
        if let mir::Lvalue::Static(ref static_) = *lvalue {
            let def_id = static_.def_id;
270
            let substs = self.ecx.tcx.intern_substs(&[]);
271
            let span = self.span;
A
Andrew Cann 已提交
272
            if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) {
273 274 275 276 277 278 279
                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");
                    }
280
                } else {
281
                    bug!("static def id doesn't point to item");
282 283
                }
            } else {
284
                let def = self.ecx.tcx.describe_def(def_id).expect("static not found");
285 286 287 288 289
                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);
                }
290
            }
291 292
        }
    }
O
Oliver Schneider 已提交
293
}