]>
Commit | Line | Data |
---|---|---|
416331ca | 1 | //! This module contains the `InterpCx` methods for executing a single step of the interpreter. |
ff7c6d11 XL |
2 | //! |
3 | //! The main entry point is the `step` method. | |
4 | ||
ba9703b0 XL |
5 | use rustc_middle::mir; |
6 | use rustc_middle::mir::interpret::{InterpResult, Scalar}; | |
c295e0f8 | 7 | use rustc_middle::ty::layout::LayoutOf; |
ff7c6d11 | 8 | |
416331ca | 9 | use super::{InterpCx, Machine}; |
ff7c6d11 | 10 | |
0731742a | 11 | /// Classify whether an operator is "left-homogeneous", i.e., the LHS has the |
b7449926 XL |
12 | /// same type as the result. |
13 | #[inline] | |
14 | fn binop_left_homogeneous(op: mir::BinOp) -> bool { | |
ba9703b0 | 15 | use rustc_middle::mir::BinOp::*; |
b7449926 | 16 | match op { |
dfeec247 XL |
17 | Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true, |
18 | Eq | Ne | Lt | Le | Gt | Ge => false, | |
b7449926 XL |
19 | } |
20 | } | |
0731742a | 21 | /// Classify whether an operator is "right-homogeneous", i.e., the RHS has the |
b7449926 XL |
22 | /// same type as the LHS. |
23 | #[inline] | |
24 | fn binop_right_homogeneous(op: mir::BinOp) -> bool { | |
ba9703b0 | 25 | use rustc_middle::mir::BinOp::*; |
b7449926 | 26 | match op { |
dfeec247 XL |
27 | Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true, |
28 | Offset | Shl | Shr => false, | |
b7449926 XL |
29 | } |
30 | } | |
31 | ||
ba9703b0 | 32 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
dc9dc135 | 33 | pub fn run(&mut self) -> InterpResult<'tcx> { |
b7449926 XL |
34 | while self.step()? {} |
35 | Ok(()) | |
ff7c6d11 XL |
36 | } |
37 | ||
9fa01778 | 38 | /// Returns `true` as long as there are more things to do. |
0bf4aa26 XL |
39 | /// |
40 | /// This is used by [priroda](https://github.com/oli-obk/priroda) | |
74b04a01 | 41 | /// |
5e7ed085 | 42 | /// This is marked `#inline(always)` to work around adversarial codegen when `opt-level = 3` |
74b04a01 | 43 | #[inline(always)] |
dc9dc135 | 44 | pub fn step(&mut self) -> InterpResult<'tcx, bool> { |
ba9703b0 | 45 | if self.stack().is_empty() { |
ff7c6d11 XL |
46 | return Ok(false); |
47 | } | |
48 | ||
5e7ed085 FG |
49 | let Ok(loc) = self.frame().loc else { |
50 | // We are unwinding and this fn has no cleanup code. | |
51 | // Just go on unwinding. | |
52 | trace!("unwinding: skipping frame"); | |
53 | self.pop_stack_frame(/* unwinding */ true)?; | |
54 | return Ok(true); | |
60c5eb7d | 55 | }; |
f9f354fc | 56 | let basic_block = &self.body().basic_blocks()[loc.block]; |
ff7c6d11 | 57 | |
f9f354fc | 58 | if let Some(stmt) = basic_block.statements.get(loc.statement_index) { |
923072b8 | 59 | let old_frames = self.frame_idx(); |
0531ce1d | 60 | self.statement(stmt)?; |
923072b8 FG |
61 | // Make sure we are not updating `statement_index` of the wrong frame. |
62 | assert_eq!(old_frames, self.frame_idx()); | |
63 | // Advance the program counter. | |
64 | self.frame_mut().loc.as_mut().unwrap().statement_index += 1; | |
ff7c6d11 XL |
65 | return Ok(true); |
66 | } | |
67 | ||
0bf4aa26 | 68 | M::before_terminator(self)?; |
0531ce1d | 69 | |
ff7c6d11 | 70 | let terminator = basic_block.terminator(); |
0531ce1d | 71 | self.terminator(terminator)?; |
ff7c6d11 XL |
72 | Ok(true) |
73 | } | |
74 | ||
3dfed10e | 75 | /// Runs the interpretation logic for the given `mir::Statement` at the current frame and |
923072b8 FG |
76 | /// statement counter. |
77 | /// | |
78 | /// This does NOT move the statement counter forward, the caller has to do that! | |
c295e0f8 | 79 | pub fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> { |
0731742a | 80 | info!("{:?}", stmt); |
ff7c6d11 | 81 | |
ba9703b0 | 82 | use rustc_middle::mir::StatementKind::*; |
ff7c6d11 | 83 | |
ba9703b0 XL |
84 | match &stmt.kind { |
85 | Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?, | |
ff7c6d11 | 86 | |
ba9703b0 XL |
87 | SetDiscriminant { place, variant_index } => { |
88 | let dest = self.eval_place(**place)?; | |
6a06907d | 89 | self.write_discriminant(*variant_index, &dest)?; |
ff7c6d11 XL |
90 | } |
91 | ||
04454e1e FG |
92 | Deinit(place) => { |
93 | let dest = self.eval_place(**place)?; | |
94 | self.write_uninit(&dest)?; | |
95 | } | |
96 | ||
ff7c6d11 XL |
97 | // Mark locals as alive |
98 | StorageLive(local) => { | |
fc512014 | 99 | self.storage_live(*local)?; |
ff7c6d11 XL |
100 | } |
101 | ||
102 | // Mark locals as dead | |
103 | StorageDead(local) => { | |
fc512014 | 104 | self.storage_dead(*local)?; |
ff7c6d11 XL |
105 | } |
106 | ||
0bf4aa26 | 107 | // No dynamic semantics attached to `FakeRead`; MIR |
94b46f34 | 108 | // interpreter is solely intended for borrowck'ed code. |
0bf4aa26 | 109 | FakeRead(..) => {} |
94b46f34 | 110 | |
a1dfa0c6 | 111 | // Stacked Borrows. |
ba9703b0 XL |
112 | Retag(kind, place) => { |
113 | let dest = self.eval_place(**place)?; | |
6a06907d XL |
114 | M::retag(self, *kind, &dest)?; |
115 | } | |
116 | ||
117 | // Call CopyNonOverlapping | |
118 | CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { src, dst, count }) => { | |
119 | let src = self.eval_operand(src, None)?; | |
120 | let dst = self.eval_operand(dst, None)?; | |
121 | let count = self.eval_operand(count, None)?; | |
17df50a5 | 122 | self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true)?; |
ff7c6d11 | 123 | } |
ff7c6d11 | 124 | |
a1dfa0c6 | 125 | // Statements we do not track. |
b7449926 | 126 | AscribeUserType(..) => {} |
0531ce1d | 127 | |
3dfed10e XL |
128 | // Currently, Miri discards Coverage statements. Coverage statements are only injected |
129 | // via an optional compile time MIR pass and have no side effects. Since Coverage | |
130 | // statements don't exist at the source level, it is safe for Miri to ignore them, even | |
131 | // for undefined behavior (UB) checks. | |
132 | // | |
133 | // A coverage counter inside a const expression (for example, a counter injected in a | |
134 | // const function) is discarded when the const is evaluated at compile time. Whether | |
135 | // this should change, and/or how to implement a const eval counter, is a subject of the | |
136 | // following issue: | |
137 | // | |
138 | // FIXME(#73156): Handle source code coverage in const eval | |
139 | Coverage(..) => {} | |
140 | ||
ff7c6d11 XL |
141 | // Defined to do nothing. These are added by optimization passes, to avoid changing the |
142 | // size of MIR constantly. | |
143 | Nop => {} | |
ff7c6d11 XL |
144 | } |
145 | ||
ff7c6d11 XL |
146 | Ok(()) |
147 | } | |
148 | ||
b7449926 XL |
149 | /// Evaluate an assignment statement. |
150 | /// | |
151 | /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue | |
152 | /// type writes its results directly into the memory specified by the place. | |
e74abb32 | 153 | pub fn eval_rvalue_into_place( |
b7449926 XL |
154 | &mut self, |
155 | rvalue: &mir::Rvalue<'tcx>, | |
ba9703b0 | 156 | place: mir::Place<'tcx>, |
dc9dc135 | 157 | ) -> InterpResult<'tcx> { |
b7449926 | 158 | let dest = self.eval_place(place)?; |
923072b8 FG |
159 | // FIXME: ensure some kind of non-aliasing between LHS and RHS? |
160 | // Also see https://github.com/rust-lang/rust/issues/68364. | |
b7449926 | 161 | |
ba9703b0 | 162 | use rustc_middle::mir::Rvalue::*; |
b7449926 | 163 | match *rvalue { |
f9f354fc | 164 | ThreadLocalRef(did) => { |
136023e0 XL |
165 | let ptr = M::thread_local_static_base_pointer(self, did)?; |
166 | self.write_pointer(ptr, &dest)?; | |
f9f354fc XL |
167 | } |
168 | ||
b7449926 XL |
169 | Use(ref operand) => { |
170 | // Avoid recomputing the layout | |
171 | let op = self.eval_operand(operand, Some(dest.layout))?; | |
6a06907d | 172 | self.copy_op(&op, &dest)?; |
b7449926 XL |
173 | } |
174 | ||
6a06907d | 175 | BinaryOp(bin_op, box (ref left, ref right)) => { |
60c5eb7d | 176 | let layout = binop_left_homogeneous(bin_op).then_some(dest.layout); |
6a06907d | 177 | let left = self.read_immediate(&self.eval_operand(left, layout)?)?; |
60c5eb7d | 178 | let layout = binop_right_homogeneous(bin_op).then_some(left.layout); |
6a06907d XL |
179 | let right = self.read_immediate(&self.eval_operand(right, layout)?)?; |
180 | self.binop_ignore_overflow(bin_op, &left, &right, &dest)?; | |
b7449926 XL |
181 | } |
182 | ||
6a06907d | 183 | CheckedBinaryOp(bin_op, box (ref left, ref right)) => { |
b7449926 | 184 | // Due to the extra boolean in the result, we can never reuse the `dest.layout`. |
6a06907d | 185 | let left = self.read_immediate(&self.eval_operand(left, None)?)?; |
60c5eb7d | 186 | let layout = binop_right_homogeneous(bin_op).then_some(left.layout); |
6a06907d XL |
187 | let right = self.read_immediate(&self.eval_operand(right, layout)?)?; |
188 | self.binop_with_overflow(bin_op, &left, &right, &dest)?; | |
b7449926 XL |
189 | } |
190 | ||
191 | UnaryOp(un_op, ref operand) => { | |
192 | // The operand always has the same type as the result. | |
6a06907d XL |
193 | let val = self.read_immediate(&self.eval_operand(operand, Some(dest.layout))?)?; |
194 | let val = self.unary_op(un_op, &val)?; | |
e1599b0c | 195 | assert_eq!(val.layout, dest.layout, "layout mismatch for result of {:?}", un_op); |
6a06907d | 196 | self.write_immediate(*val, &dest)?; |
b7449926 XL |
197 | } |
198 | ||
04454e1e FG |
199 | Aggregate(box ref kind, ref operands) => { |
200 | assert!(matches!(kind, mir::AggregateKind::Array(..))); | |
201 | ||
202 | for (field_index, operand) in operands.iter().enumerate() { | |
b7449926 | 203 | let op = self.eval_operand(operand, None)?; |
c295e0f8 XL |
204 | let field_dest = self.place_field(&dest, field_index)?; |
205 | self.copy_op(&op, &field_dest)?; | |
b7449926 XL |
206 | } |
207 | } | |
208 | ||
209 | Repeat(ref operand, _) => { | |
17df50a5 XL |
210 | let src = self.eval_operand(operand, None)?; |
211 | assert!(!src.layout.is_unsized()); | |
6a06907d | 212 | let dest = self.force_allocation(&dest)?; |
a1dfa0c6 | 213 | let length = dest.len(self)?; |
b7449926 | 214 | |
17df50a5 XL |
215 | if length == 0 { |
216 | // Nothing to copy... but let's still make sure that `dest` as a place is valid. | |
04454e1e | 217 | self.get_place_alloc_mut(&dest)?; |
17df50a5 XL |
218 | } else { |
219 | // Write the src to the first element. | |
6a06907d | 220 | let first = self.mplace_field(&dest, 0)?; |
17df50a5 XL |
221 | self.copy_op(&src, &first.into())?; |
222 | ||
223 | // This is performance-sensitive code for big static/const arrays! So we | |
224 | // avoid writing each operand individually and instead just make many copies | |
225 | // of the first element. | |
226 | let elem_size = first.layout.size; | |
227 | let first_ptr = first.ptr; | |
136023e0 | 228 | let rest_ptr = first_ptr.offset(elem_size, self)?; |
a2a8927a XL |
229 | // For the alignment of `rest_ptr`, we crucially do *not* use `first.align` as |
230 | // that place might be more aligned than its type mandates (a `u8` array could | |
231 | // be 4-aligned if it sits at the right spot in a struct). Instead we use | |
232 | // `first.layout.align`, i.e., the alignment given by the type. | |
04454e1e | 233 | self.mem_copy_repeatedly( |
17df50a5 XL |
234 | first_ptr, |
235 | first.align, | |
236 | rest_ptr, | |
a2a8927a | 237 | first.layout.align.abi, |
17df50a5 XL |
238 | elem_size, |
239 | length - 1, | |
240 | /*nonoverlapping:*/ true, | |
241 | )?; | |
b7449926 XL |
242 | } |
243 | } | |
244 | ||
ba9703b0 | 245 | Len(place) => { |
b7449926 | 246 | let src = self.eval_place(place)?; |
6a06907d | 247 | let mplace = self.force_allocation(&src)?; |
a1dfa0c6 | 248 | let len = mplace.len(self)?; |
6a06907d | 249 | self.write_scalar(Scalar::from_machine_usize(len, self), &dest)?; |
b7449926 XL |
250 | } |
251 | ||
ba9703b0 | 252 | AddressOf(_, place) | Ref(_, _, place) => { |
b7449926 | 253 | let src = self.eval_place(place)?; |
6a06907d | 254 | let place = self.force_allocation(&src)?; |
136023e0 | 255 | self.write_immediate(place.to_ref(self), &dest)?; |
b7449926 XL |
256 | } |
257 | ||
c295e0f8 | 258 | NullaryOp(null_op, ty) => { |
a2a8927a | 259 | let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty)?; |
b7449926 | 260 | let layout = self.layout_of(ty)?; |
5869c6ff XL |
261 | if layout.is_unsized() { |
262 | // FIXME: This should be a span_bug (#80742) | |
263 | self.tcx.sess.delay_span_bug( | |
264 | self.frame().current_span(), | |
c295e0f8 | 265 | &format!("Nullary MIR operator called for unsized type {}", ty), |
5869c6ff XL |
266 | ); |
267 | throw_inval!(SizeOfUnsizedType(ty)); | |
268 | } | |
c295e0f8 XL |
269 | let val = match null_op { |
270 | mir::NullOp::SizeOf => layout.size.bytes(), | |
271 | mir::NullOp::AlignOf => layout.align.abi.bytes(), | |
c295e0f8 XL |
272 | }; |
273 | self.write_scalar(Scalar::from_machine_usize(val, self), &dest)?; | |
274 | } | |
275 | ||
276 | ShallowInitBox(ref operand, _) => { | |
277 | let src = self.eval_operand(operand, None)?; | |
278 | let v = self.read_immediate(&src)?; | |
279 | self.write_immediate(*v, &dest)?; | |
b7449926 XL |
280 | } |
281 | ||
f9f354fc | 282 | Cast(cast_kind, ref operand, cast_ty) => { |
b7449926 | 283 | let src = self.eval_operand(operand, None)?; |
a2a8927a XL |
284 | let cast_ty = |
285 | self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty)?; | |
6a06907d | 286 | self.cast(&src, cast_kind, cast_ty, &dest)?; |
b7449926 XL |
287 | } |
288 | ||
ba9703b0 | 289 | Discriminant(place) => { |
9fa01778 | 290 | let op = self.eval_place_to_op(place, None)?; |
6a06907d XL |
291 | let discr_val = self.read_discriminant(&op)?.0; |
292 | self.write_scalar(discr_val, &dest)?; | |
b7449926 XL |
293 | } |
294 | } | |
295 | ||
3dfed10e | 296 | trace!("{:?}", self.dump_place(*dest)); |
b7449926 XL |
297 | |
298 | Ok(()) | |
299 | } | |
300 | ||
923072b8 | 301 | /// Evaluate the given terminator. Will also adjust the stack frame and statement position accordingly. |
dc9dc135 | 302 | fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> { |
0731742a | 303 | info!("{:?}", terminator.kind); |
60c5eb7d | 304 | |
ff7c6d11 | 305 | self.eval_terminator(terminator)?; |
ba9703b0 | 306 | if !self.stack().is_empty() { |
3dfed10e | 307 | if let Ok(loc) = self.frame().loc { |
f9f354fc | 308 | info!("// executing {:?}", loc.block); |
60c5eb7d | 309 | } |
ff7c6d11 XL |
310 | } |
311 | Ok(()) | |
312 | } | |
ff7c6d11 | 313 | } |