step.rs 11.7 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;
9
use rustc::traits::Reveal;
S
Scott Olson 已提交
10
use rustc::ty::layout::Layout;
11 12 13
use rustc::ty::{subst, self};

use error::{EvalResult, EvalError};
14
use eval_context::{EvalContext, StackPopCleanup};
15
use lvalue::{Global, GlobalId, Lvalue};
16
use value::{Value, PrimVal};
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
                    mir,
43
                    Some(Lvalue::zst()),
R
Ralf Jung 已提交
44 45
                    StackPopCleanup::None,
                )?;
46 47 48
                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);
49
                self.write_primval(dest, ptr, 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
            // 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),
O
Oliver Schneider 已提交
133
                    _ => return Err(EvalError::Unimplemented("Storage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type
134
                };
135
                let old_val = match stmt.kind {
136
                    StorageLive(_) => self.stack[frame].storage_live(local)?,
137 138
                    StorageDead(_) =>  self.stack[frame].storage_dead(local)?,
                    _ => bug!("We already checked that we are a storage stmt")
139
                };
140
                self.deallocate_local(old_val)?;
141
            }
S
Scott Olson 已提交
142

O
Oliver Schneider 已提交
143
            // Just a borrowck thing
D
David Renshaw 已提交
144 145
            EndRegion(..) => {}

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

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

153 154 155 156
        self.frame_mut().stmt += 1;
        Ok(())
    }

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

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

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

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

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

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