]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/operand.rs
9a36abdb7cfa0b0c2e0b2bc0b039c18c4061d878
[rust.git] / src / librustc_mir / interpret / operand.rs
1 //! Functions concerning immediate values and operands, and reading from operands.
2 //! All high-level functions to read from memory work on operands as sources.
3
4 use std::convert::TryInto;
5
6 use rustc::mir;
7 use rustc::ty::layout::{self, Align, LayoutOf, TyLayout, HasDataLayout, IntegerExt};
8 use rustc_data_structures::indexed_vec::Idx;
9
10 use rustc::mir::interpret::{
11     GlobalId, ConstValue, Scalar, EvalResult, Pointer, ScalarMaybeUndef, EvalErrorKind
12 };
13 use super::{EvalContext, Machine, MemPlace, MPlaceTy, PlaceExtra, MemoryKind};
14
15 /// A `Value` represents a single immediate self-contained Rust value.
16 ///
17 /// For optimization of a few very common cases, there is also a representation for a pair of
18 /// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
19 /// operations and fat pointers. This idea was taken from rustc's codegen.
20 /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
21 /// defined on `Value`, and do not have to work with a `Place`.
22 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
23 pub enum Value {
24     Scalar(ScalarMaybeUndef),
25     ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef),
26 }
27
28 impl<'tcx> Value {
29     pub fn new_slice(
30         val: Scalar,
31         len: u64,
32         cx: impl HasDataLayout
33     ) -> Self {
34         Value::ScalarPair(val.into(), Scalar::Bits {
35             bits: len as u128,
36             size: cx.data_layout().pointer_size.bytes() as u8,
37         }.into())
38     }
39
40     pub fn new_dyn_trait(val: Scalar, vtable: Pointer) -> Self {
41         Value::ScalarPair(val.into(), Scalar::Ptr(vtable).into())
42     }
43
44     pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef {
45         match self {
46             Value::Scalar(val) => val,
47             Value::ScalarPair(..) => bug!("Got a fat pointer where a scalar was expected"),
48         }
49     }
50
51     pub fn to_scalar(self) -> EvalResult<'tcx, Scalar> {
52         self.to_scalar_or_undef().not_undef()
53     }
54
55     /// Convert the value into a pointer (or a pointer-sized integer).
56     pub fn to_scalar_ptr(self) -> EvalResult<'tcx, Scalar> {
57         match self {
58             Value::Scalar(ptr) |
59             Value::ScalarPair(ptr, _) => ptr.not_undef(),
60         }
61     }
62
63     pub fn to_scalar_dyn_trait(self) -> EvalResult<'tcx, (Scalar, Pointer)> {
64         match self {
65             Value::ScalarPair(ptr, vtable) =>
66                 Ok((ptr.not_undef()?, vtable.to_ptr()?)),
67             _ => bug!("expected ptr and vtable, got {:?}", self),
68         }
69     }
70
71     pub fn to_scalar_slice(self, cx: impl HasDataLayout) -> EvalResult<'tcx, (Scalar, u64)> {
72         match self {
73             Value::ScalarPair(ptr, val) => {
74                 let len = val.to_bits(cx.data_layout().pointer_size)?;
75                 Ok((ptr.not_undef()?, len as u64))
76             }
77             _ => bug!("expected ptr and length, got {:?}", self),
78         }
79     }
80 }
81
82 // ScalarPair needs a type to interpret, so we often have a value and a type together
83 // as input for binary and cast operations.
84 #[derive(Copy, Clone, Debug)]
85 pub struct ValTy<'tcx> {
86     pub value: Value,
87     pub layout: TyLayout<'tcx>,
88 }
89
90 impl<'tcx> ::std::ops::Deref for ValTy<'tcx> {
91     type Target = Value;
92     fn deref(&self) -> &Value {
93         &self.value
94     }
95 }
96
97 /// An `Operand` is the result of computing a `mir::Operand`. It can be immediate,
98 /// or still in memory.  The latter is an optimization, to delay reading that chunk of
99 /// memory and to avoid having to store arbitrary-sized data here.
100 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
101 pub enum Operand {
102     Immediate(Value),
103     Indirect(MemPlace),
104 }
105
106 impl Operand {
107     #[inline]
108     pub fn from_ptr(ptr: Pointer, align: Align) -> Self {
109         Operand::Indirect(MemPlace::from_ptr(ptr, align))
110     }
111
112     #[inline]
113     pub fn from_scalar_value(val: Scalar) -> Self {
114         Operand::Immediate(Value::Scalar(val.into()))
115     }
116
117     #[inline]
118     pub fn to_mem_place(self) -> MemPlace {
119         match self {
120             Operand::Indirect(mplace) => mplace,
121             _ => bug!("to_mem_place: expected Operand::Indirect, got {:?}", self),
122
123         }
124     }
125
126     #[inline]
127     pub fn to_immediate(self) -> Value {
128         match self {
129             Operand::Immediate(val) => val,
130             _ => bug!("to_immediate: expected Operand::Immediate, got {:?}", self),
131
132         }
133     }
134 }
135
136 #[derive(Copy, Clone, Debug)]
137 pub struct OpTy<'tcx> {
138     pub op: Operand,
139     pub layout: TyLayout<'tcx>,
140 }
141
142 impl<'tcx> ::std::ops::Deref for OpTy<'tcx> {
143     type Target = Operand;
144     fn deref(&self) -> &Operand {
145         &self.op
146     }
147 }
148
149 impl<'tcx> From<MPlaceTy<'tcx>> for OpTy<'tcx> {
150     fn from(mplace: MPlaceTy<'tcx>) -> Self {
151         OpTy {
152             op: Operand::Indirect(*mplace),
153             layout: mplace.layout
154         }
155     }
156 }
157
158 impl<'tcx> From<ValTy<'tcx>> for OpTy<'tcx> {
159     fn from(val: ValTy<'tcx>) -> Self {
160         OpTy {
161             op: Operand::Immediate(val.value),
162             layout: val.layout
163         }
164     }
165 }
166
167 impl<'tcx> OpTy<'tcx> {
168     #[inline]
169     pub fn from_ptr(ptr: Pointer, align: Align, layout: TyLayout<'tcx>) -> Self {
170         OpTy { op: Operand::from_ptr(ptr, align), layout }
171     }
172
173     #[inline]
174     pub fn from_aligned_ptr(ptr: Pointer, layout: TyLayout<'tcx>) -> Self {
175         OpTy { op: Operand::from_ptr(ptr, layout.align), layout }
176     }
177
178     #[inline]
179     pub fn from_scalar_value(val: Scalar, layout: TyLayout<'tcx>) -> Self {
180         OpTy { op: Operand::Immediate(Value::Scalar(val.into())), layout }
181     }
182 }
183
184 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
185     /// Try reading a value in memory; this is interesting particularily for ScalarPair.
186     /// Return None if the layout does not permit loading this as a value.
187     fn try_read_value_from_ptr(
188         &self,
189         ptr: Scalar,
190         ptr_align: Align,
191         layout: TyLayout<'tcx>,
192     ) -> EvalResult<'tcx, Option<Value>> {
193         self.memory.check_align(ptr, ptr_align)?;
194
195         if layout.size.bytes() == 0 {
196             return Ok(Some(Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { bits: 0, size: 0 }))));
197         }
198
199         let ptr = ptr.to_ptr()?;
200
201         match layout.abi {
202             layout::Abi::Scalar(..) => {
203                 let scalar = self.memory.read_scalar(ptr, ptr_align, layout.size)?;
204                 Ok(Some(Value::Scalar(scalar)))
205             }
206             layout::Abi::ScalarPair(ref a, ref b) => {
207                 let (a, b) = (&a.value, &b.value);
208                 let (a_size, b_size) = (a.size(self), b.size(self));
209                 let a_ptr = ptr;
210                 let b_offset = a_size.abi_align(b.align(self));
211                 assert!(b_offset.bytes() > 0); // we later use the offset to test which field to use
212                 let b_ptr = ptr.offset(b_offset, self)?.into();
213                 let a_val = self.memory.read_scalar(a_ptr, ptr_align, a_size)?;
214                 let b_val = self.memory.read_scalar(b_ptr, ptr_align, b_size)?;
215                 Ok(Some(Value::ScalarPair(a_val, b_val)))
216             }
217             _ => Ok(None),
218         }
219     }
220
221     /// Try returning an immediate value for the operand.
222     /// If the layout does not permit loading this as a value, return where in memory
223     /// we can find the data.
224     /// Note that for a given layout, this operation will either always fail or always
225     /// succeed!  Whether it succeeds depends on whether the layout can be represented
226     /// in a `Value`, not on which data is stored there currently.
227     pub(super) fn try_read_value(
228         &self,
229         OpTy { op: src, layout } : OpTy<'tcx>,
230     ) -> EvalResult<'tcx, Result<Value, MemPlace>> {
231         match src {
232             Operand::Indirect(mplace) => {
233                 if mplace.extra == PlaceExtra::None {
234                     if let Some(val) =
235                         self.try_read_value_from_ptr(mplace.ptr, mplace.align, layout)?
236                     {
237                         return Ok(Ok(val));
238                     }
239                 }
240                 Ok(Err(mplace))
241             },
242             Operand::Immediate(val) => Ok(Ok(val)),
243         }
244     }
245
246     /// Read a value from a place, asserting that that is possible with the given layout.
247     #[inline(always)]
248     pub fn read_value(&self, op: OpTy<'tcx>) -> EvalResult<'tcx, ValTy<'tcx>> {
249         if let Ok(value) = self.try_read_value(op)? {
250             Ok(ValTy { value, layout: op.layout })
251         } else {
252             bug!("primitive read failed for type: {:?}", op.layout.ty);
253         }
254     }
255
256     /// Read a scalar from a place
257     pub fn read_scalar(&self, op : OpTy<'tcx>) -> EvalResult<'tcx, ScalarMaybeUndef> {
258         match *self.read_value(op)? {
259             Value::ScalarPair(..) => bug!("got ScalarPair for type: {:?}", op.layout.ty),
260             Value::Scalar(val) => Ok(val),
261         }
262     }
263
264     pub fn uninit_operand(&mut self, layout: TyLayout<'tcx>) -> EvalResult<'tcx, Operand> {
265         // FIXME: Aren't we supposed to also be immediate for a ZST?
266         // This decides which types we will use the Immediate optimization for, and hence should
267         // match what `try_read_value` and `eval_place_to_op` support.
268         Ok(match layout.abi {
269             layout::Abi::Scalar(..) =>
270                 Operand::Immediate(Value::Scalar(ScalarMaybeUndef::Undef)),
271             layout::Abi::ScalarPair(..) =>
272                 Operand::Immediate(Value::ScalarPair(
273                     ScalarMaybeUndef::Undef,
274                     ScalarMaybeUndef::Undef,
275                 )),
276             _ => {
277                 trace!("Forcing allocation for local of type {:?}", layout.ty);
278                 Operand::Indirect(
279                     *self.allocate(layout, MemoryKind::Stack)?
280                 )
281             }
282         })
283     }
284
285     /// Projection functions
286     pub fn operand_field(
287         &self,
288         op: OpTy<'tcx>,
289         field: u64,
290     ) -> EvalResult<'tcx, OpTy<'tcx>> {
291         let base = match op.try_as_mplace() {
292             Ok(mplace) => {
293                 // The easy case
294                 let field = self.mplace_field(mplace, field)?;
295                 return Ok(field.into());
296             },
297             Err(value) => value
298         };
299
300         let field = field.try_into().unwrap();
301         let field_layout = op.layout.field(self, field)?;
302         if field_layout.size.bytes() == 0 {
303             let val = Value::Scalar(Scalar::zst().into());
304             return Ok(OpTy { op: Operand::Immediate(val), layout: field_layout });
305         }
306         let offset = op.layout.fields.offset(field);
307         let value = match base {
308             // the field covers the entire type
309             _ if offset.bytes() == 0 && field_layout.size == op.layout.size => base,
310             // extract fields from types with `ScalarPair` ABI
311             Value::ScalarPair(a, b) => {
312                 let val = if offset.bytes() == 0 { a } else { b };
313                 Value::Scalar(val)
314             },
315             Value::Scalar(val) =>
316                 bug!("field access on non aggregate {:#?}, {:#?}", val, op.layout),
317         };
318         Ok(OpTy { op: Operand::Immediate(value), layout: field_layout })
319     }
320
321     pub(super) fn operand_downcast(
322         &self,
323         op: OpTy<'tcx>,
324         variant: usize,
325     ) -> EvalResult<'tcx, OpTy<'tcx>> {
326         // Downcasts only change the layout
327         Ok(match op.try_as_mplace() {
328             Ok(mplace) => {
329                 self.mplace_downcast(mplace, variant)?.into()
330             },
331             Err(..) => {
332                 let layout = op.layout.for_variant(self, variant);
333                 OpTy { layout, ..op }
334             }
335         })
336     }
337
338     // Take an operand, representing a pointer, and dereference it -- that
339     // will always be a MemPlace.
340     pub(super) fn deref_operand(
341         &self,
342         src: OpTy<'tcx>,
343     ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
344         let val = self.read_value(src)?;
345         trace!("deref to {} on {:?}", val.layout.ty, val);
346         Ok(self.ref_to_mplace(val)?)
347     }
348
349     pub fn operand_projection(
350         &self,
351         base: OpTy<'tcx>,
352         proj_elem: &mir::PlaceElem<'tcx>,
353     ) -> EvalResult<'tcx, OpTy<'tcx>> {
354         use rustc::mir::ProjectionElem::*;
355         Ok(match *proj_elem {
356             Field(field, _) => self.operand_field(base, field.index() as u64)?,
357             Downcast(_, variant) => self.operand_downcast(base, variant)?,
358             Deref => self.deref_operand(base)?.into(),
359             // The rest should only occur as mplace, we do not use Immediates for types
360             // allowing such operations.  This matches place_projection forcing an allocation.
361             Subslice { .. } | ConstantIndex { .. } | Index(_) => {
362                 let mplace = base.to_mem_place();
363                 self.mplace_projection(mplace, proj_elem)?.into()
364             }
365         })
366     }
367
368     // Evaluate a place with the goal of reading from it.  This lets us sometimes
369     // avoid allocations.
370     fn eval_place_to_op(
371         &mut self,
372         mir_place: &mir::Place<'tcx>,
373     ) -> EvalResult<'tcx, OpTy<'tcx>> {
374         use rustc::mir::Place::*;
375         Ok(match *mir_place {
376             Local(mir::RETURN_PLACE) => return err!(ReadFromReturnPointer),
377             Local(local) => {
378                 let op = *self.frame().locals[local].access()?;
379                 OpTy { op, layout: self.layout_of_local(self.cur_frame(), local)? }
380             },
381
382             Projection(ref proj) => {
383                 let op = self.eval_place_to_op(&proj.base)?;
384                 self.operand_projection(op, &proj.elem)?
385             }
386
387             // Everything else is an mplace, so we just call `eval_place`.
388             // Note that getting an mplace for a static aways requires `&mut`,
389             // so this does not "cost" us anything in terms if mutability.
390             Promoted(_) | Static(_) => {
391                 let place = self.eval_place(mir_place)?;
392                 place.to_mem_place().into()
393             }
394         })
395     }
396
397     /// Evaluate the operand, returning a place where you can then find the data.
398     pub fn eval_operand(&mut self, mir_op: &mir::Operand<'tcx>) -> EvalResult<'tcx, OpTy<'tcx>> {
399         use rustc::mir::Operand::*;
400         let op = match *mir_op {
401             // FIXME: do some more logic on `move` to invalidate the old location
402             Copy(ref place) |
403             Move(ref place) =>
404                 self.eval_place_to_op(place)?,
405
406             Constant(ref constant) => {
407                 let ty = self.monomorphize(mir_op.ty(self.mir(), *self.tcx), self.substs());
408                 let layout = self.layout_of(ty)?;
409                 let op = self.const_value_to_op(constant.literal.val)?;
410                 OpTy { op, layout }
411             }
412         };
413         trace!("{:?}: {:?}", mir_op, *op);
414         Ok(op)
415     }
416
417     /// Evaluate a bunch of operands at once
418     pub(crate) fn eval_operands(
419         &mut self,
420         ops: &[mir::Operand<'tcx>],
421     ) -> EvalResult<'tcx, Vec<OpTy<'tcx>>> {
422         ops.into_iter()
423             .map(|op| self.eval_operand(op))
424             .collect()
425     }
426
427     // Also used e.g. when miri runs into a constant.
428     // Unfortunately, this needs an `&mut` to be able to allocate a copy of a `ByRef`
429     // constant.  This bleeds up to `eval_operand` needing `&mut`.
430     pub fn const_value_to_op(
431         &mut self,
432         val: ConstValue<'tcx>,
433     ) -> EvalResult<'tcx, Operand> {
434         match val {
435             ConstValue::Unevaluated(def_id, substs) => {
436                 let instance = self.resolve(def_id, substs)?;
437                 self.global_to_op(GlobalId {
438                     instance,
439                     promoted: None,
440                 })
441             }
442             ConstValue::ByRef(alloc, offset) => {
443                 // FIXME: Allocate new AllocId for all constants inside
444                 let id = self.memory.allocate_value(alloc.clone(), MemoryKind::Stack)?;
445                 Ok(Operand::from_ptr(Pointer::new(id, offset), alloc.align))
446             },
447             ConstValue::ScalarPair(a, b) =>
448                 Ok(Operand::Immediate(Value::ScalarPair(a.into(), b))),
449             ConstValue::Scalar(x) =>
450                 Ok(Operand::Immediate(Value::Scalar(x.into()))),
451         }
452     }
453
454     pub(super) fn global_to_op(&mut self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, Operand> {
455         let cv = self.const_eval(gid)?;
456         self.const_value_to_op(cv.val)
457     }
458
459     /// We cannot do self.read_value(self.eval_operand) due to eval_operand taking &mut self,
460     /// so this helps avoid unnecessary let.
461     pub fn eval_operand_and_read_valty(
462         &mut self,
463         op: &mir::Operand<'tcx>,
464     ) -> EvalResult<'tcx, ValTy<'tcx>> {
465         let op = self.eval_operand(op)?;
466         self.read_value(op)
467     }
468     pub fn eval_operand_and_read_scalar(
469         &mut self,
470         op: &mir::Operand<'tcx>,
471     ) -> EvalResult<'tcx, ScalarMaybeUndef> {
472         Ok(self.eval_operand_and_read_valty(op)?.to_scalar_or_undef())
473     }
474
475     /// reads a tag and produces the corresponding variant index
476     pub fn read_discriminant_as_variant_index(
477         &self,
478         rval: OpTy<'tcx>,
479     ) -> EvalResult<'tcx, usize> {
480         match rval.layout.variants {
481             layout::Variants::Single { index } => Ok(index),
482             layout::Variants::Tagged { .. } => {
483                 let discr_val = self.read_discriminant_value(rval)?;
484                 rval.layout.ty
485                     .ty_adt_def()
486                     .expect("tagged layout for non adt")
487                     .discriminants(self.tcx.tcx)
488                     .position(|var| var.val == discr_val)
489                     .ok_or_else(|| EvalErrorKind::InvalidDiscriminant.into())
490             }
491             layout::Variants::NicheFilling { .. } => {
492                 let discr_val = self.read_discriminant_value(rval)?;
493                 assert_eq!(discr_val as usize as u128, discr_val);
494                 Ok(discr_val as usize)
495             },
496         }
497     }
498
499     pub fn read_discriminant_value(
500         &self,
501         rval: OpTy<'tcx>,
502     ) -> EvalResult<'tcx, u128> {
503         trace!("read_discriminant_value {:#?}", rval.layout);
504         if rval.layout.abi == layout::Abi::Uninhabited {
505             return err!(Unreachable);
506         }
507
508         match rval.layout.variants {
509             layout::Variants::Single { index } => {
510                 let discr_val = rval.layout.ty.ty_adt_def().map_or(
511                     index as u128,
512                     |def| def.discriminant_for_variant(*self.tcx, index).val);
513                 return Ok(discr_val);
514             }
515             layout::Variants::Tagged { .. } |
516             layout::Variants::NicheFilling { .. } => {},
517         }
518         let discr_op = self.operand_field(rval, 0)?;
519         let discr_val = self.read_value(discr_op)?;
520         trace!("discr value: {:?}", discr_val);
521         let raw_discr = discr_val.to_scalar()?;
522         Ok(match rval.layout.variants {
523             layout::Variants::Single { .. } => bug!(),
524             // FIXME: We should catch invalid discriminants here!
525             layout::Variants::Tagged { .. } => {
526                 if discr_val.layout.ty.is_signed() {
527                     let i = raw_discr.to_bits(discr_val.layout.size)? as i128;
528                     // going from layout tag type to typeck discriminant type
529                     // requires first sign extending with the layout discriminant
530                     let shift = 128 - discr_val.layout.size.bits();
531                     let sexted = (i << shift) >> shift;
532                     // and then zeroing with the typeck discriminant type
533                     let discr_ty = rval.layout.ty
534                         .ty_adt_def().expect("tagged layout corresponds to adt")
535                         .repr
536                         .discr_type();
537                     let discr_ty = layout::Integer::from_attr(self.tcx.tcx, discr_ty);
538                     let shift = 128 - discr_ty.size().bits();
539                     let truncatee = sexted as u128;
540                     (truncatee << shift) >> shift
541                 } else {
542                     raw_discr.to_bits(discr_val.layout.size)?
543                 }
544             },
545             layout::Variants::NicheFilling {
546                 dataful_variant,
547                 ref niche_variants,
548                 niche_start,
549                 ..
550             } => {
551                 let variants_start = *niche_variants.start() as u128;
552                 let variants_end = *niche_variants.end() as u128;
553                 match raw_discr {
554                     Scalar::Ptr(_) => {
555                         assert!(niche_start == 0);
556                         assert!(variants_start == variants_end);
557                         dataful_variant as u128
558                     },
559                     Scalar::Bits { bits: raw_discr, size } => {
560                         assert_eq!(size as u64, discr_val.layout.size.bytes());
561                         let discr = raw_discr.wrapping_sub(niche_start)
562                             .wrapping_add(variants_start);
563                         if variants_start <= discr && discr <= variants_end {
564                             discr
565                         } else {
566                             dataful_variant as u128
567                         }
568                     },
569                 }
570             }
571         })
572     }
573
574 }