place.rs 29.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

R
Ralf Jung 已提交
11 12 13 14 15 16
//! Computations on places -- field projections, going from mir::Place, and writing
//! into a place.
//! All high-level functions to write to memory work on places as destinations.

use std::convert::TryFrom;

17
use rustc::mir;
R
Ralf Jung 已提交
18
use rustc::ty::{self, Ty};
19
use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout};
O
Oliver Schneider 已提交
20

R
Ralf Jung 已提交
21
use rustc::mir::interpret::{
22
    GlobalId, AllocId, Scalar, EvalResult, Pointer, PointerArithmetic
R
Ralf Jung 已提交
23
};
24
use super::{EvalContext, Machine, Value, ValTy, ScalarMaybeUndef, Operand, OpTy, MemoryKind};
R
Ralf Jung 已提交
25 26

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
27
pub struct MemPlace<Id=AllocId> {
R
Ralf Jung 已提交
28 29 30
    /// A place may have an integral pointer for ZSTs, and since it might
    /// be turned back into a reference before ever being dereferenced.
    /// However, it may never be undef.
31
    pub ptr: Scalar<Id>,
R
Ralf Jung 已提交
32
    pub align: Align,
33
    /// Metadata for unsized places.  Interpretation is up to the type.
R
Ralf Jung 已提交
34 35
    /// Must not be present for sized types, but can be missing for unsized types
    /// (e.g. `extern type`).
36
    pub extra: Option<Scalar<Id>>,
R
Ralf Jung 已提交
37
}
O
Oliver Schneider 已提交
38

39
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
40
pub enum Place<Id=AllocId> {
41
    /// A place referring to a value allocated in the `Memory` system.
42
    Ptr(MemPlace<Id>),
O
Oliver Schneider 已提交
43

R
Ralf Jung 已提交
44 45 46 47 48 49
    /// To support alloc-free locals, we are able to write directly to a local.
    /// (Without that optimization, we'd just always be a `MemPlace`.)
    Local {
        frame: usize,
        local: mir::Local,
    },
O
Oliver Schneider 已提交
50 51
}

R
Ralf Jung 已提交
52 53 54 55 56 57 58 59
#[derive(Copy, Clone, Debug)]
pub struct PlaceTy<'tcx> {
    place: Place,
    pub layout: TyLayout<'tcx>,
}

impl<'tcx> ::std::ops::Deref for PlaceTy<'tcx> {
    type Target = Place;
60
    #[inline(always)]
R
Ralf Jung 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74
    fn deref(&self) -> &Place {
        &self.place
    }
}

/// A MemPlace with its layout. Constructing it is only possible in this module.
#[derive(Copy, Clone, Debug)]
pub struct MPlaceTy<'tcx> {
    mplace: MemPlace,
    pub layout: TyLayout<'tcx>,
}

impl<'tcx> ::std::ops::Deref for MPlaceTy<'tcx> {
    type Target = MemPlace;
75
    #[inline(always)]
R
Ralf Jung 已提交
76 77
    fn deref(&self) -> &MemPlace {
        &self.mplace
O
Oliver Schneider 已提交
78
    }
R
Ralf Jung 已提交
79 80 81
}

impl<'tcx> From<MPlaceTy<'tcx>> for PlaceTy<'tcx> {
82
    #[inline(always)]
R
Ralf Jung 已提交
83 84 85 86 87 88 89
    fn from(mplace: MPlaceTy<'tcx>) -> Self {
        PlaceTy {
            place: Place::Ptr(mplace.mplace),
            layout: mplace.layout
        }
    }
}
O
Oliver Schneider 已提交
90

R
Ralf Jung 已提交
91 92 93 94
impl MemPlace {
    #[inline(always)]
    pub fn from_scalar_ptr(ptr: Scalar, align: Align) -> Self {
        MemPlace {
95 96
            ptr,
            align,
97
            extra: None,
O
Oliver Schneider 已提交
98 99 100
        }
    }

R
Ralf Jung 已提交
101 102 103 104 105 106 107
    #[inline(always)]
    pub fn from_ptr(ptr: Pointer, align: Align) -> Self {
        Self::from_scalar_ptr(ptr.into(), align)
    }

    #[inline(always)]
    pub fn to_scalar_ptr_align(self) -> (Scalar, Align) {
108
        assert_eq!(self.extra, None);
R
Ralf Jung 已提交
109 110 111 112 113 114
        (self.ptr, self.align)
    }

    /// Extract the ptr part of the mplace
    #[inline(always)]
    pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
B
Bernardo Meurer 已提交
115 116 117
        // At this point, we forget about the alignment information --
        // the place has been turned into a reference, and no matter where it came from,
        // it now must be aligned.
R
Ralf Jung 已提交
118 119 120 121 122
        self.to_scalar_ptr_align().0.to_ptr()
    }

    /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
    /// This is the inverse of `ref_to_mplace`.
123
    pub fn to_ref(self) -> Value {
R
Ralf Jung 已提交
124 125 126
        // We ignore the alignment of the place here -- special handling for packed structs ends
        // at the `&` operator.
        match self.extra {
127 128
            None => Value::Scalar(self.ptr.into()),
            Some(extra) => Value::ScalarPair(self.ptr.into(), extra.into()),
R
Ralf Jung 已提交
129 130 131 132 133 134 135 136 137 138 139
        }
    }
}

impl<'tcx> MPlaceTy<'tcx> {
    #[inline]
    fn from_aligned_ptr(ptr: Pointer, layout: TyLayout<'tcx>) -> Self {
        MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align), layout }
    }

    #[inline]
140
    pub(super) fn len(self, cx: impl HasDataLayout) -> EvalResult<'tcx, u64> {
141 142 143 144 145 146
        if self.layout.is_unsized() {
            // We need to consult `extra` metadata
            match self.layout.ty.sty {
                ty::Slice(..) | ty::Str =>
                    return self.extra.unwrap().to_usize(cx),
                _ => bug!("len not supported on unsized type {:?}", self.layout.ty),
147
            }
148 149 150 151 152 153
        } else {
            // Go through the layout.  There are lots of types that support a length,
            // e.g. SIMD types.
            match self.layout.fields {
                layout::FieldPlacement::Array { count, .. } => Ok(count),
                _ => bug!("len not supported on sized type {:?}", self.layout.ty),
154 155 156 157 158 159 160 161 162
            }
        }
    }

    #[inline]
    pub(super) fn vtable(self) -> EvalResult<'tcx, Pointer> {
        match self.layout.ty.sty {
            ty::Dynamic(..) => self.extra.unwrap().to_ptr(),
            _ => bug!("vtable not supported on type {:?}", self.layout.ty),
R
Ralf Jung 已提交
163 164 165 166 167
        }
    }
}

impl<'tcx> OpTy<'tcx> {
168
    #[inline(always)]
R
Ralf Jung 已提交
169 170 171 172 173 174 175
    pub fn try_as_mplace(self) -> Result<MPlaceTy<'tcx>, Value> {
        match *self {
            Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }),
            Operand::Immediate(value) => Err(value),
        }
    }

176
    #[inline(always)]
R
Ralf Jung 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    pub fn to_mem_place(self) -> MPlaceTy<'tcx> {
        self.try_as_mplace().unwrap()
    }
}

impl<'tcx> Place {
    /// Produces a Place that will error if attempted to be read from or written to
    #[inline]
    pub fn null(cx: impl HasDataLayout) -> Self {
        Self::from_scalar_ptr(Scalar::ptr_null(cx), Align::from_bytes(1, 1).unwrap())
    }

    #[inline]
    pub fn from_scalar_ptr(ptr: Scalar, align: Align) -> Self {
        Place::Ptr(MemPlace::from_scalar_ptr(ptr, align))
    }

    #[inline]
195
    pub fn from_ptr(ptr: Pointer, align: Align) -> Self {
R
Ralf Jung 已提交
196
        Place::Ptr(MemPlace::from_ptr(ptr, align))
O
Oliver Schneider 已提交
197 198
    }

R
Ralf Jung 已提交
199 200
    #[inline]
    pub fn to_mem_place(self) -> MemPlace {
O
Oliver Schneider 已提交
201
        match self {
R
Ralf Jung 已提交
202 203
            Place::Ptr(mplace) => mplace,
            _ => bug!("to_mem_place: expected Place::Ptr, got {:?}", self),
O
Oliver Schneider 已提交
204 205 206 207

        }
    }

R
Ralf Jung 已提交
208 209 210
    #[inline]
    pub fn to_scalar_ptr_align(self) -> (Scalar, Align) {
        self.to_mem_place().to_scalar_ptr_align()
211
    }
212

R
Ralf Jung 已提交
213
    #[inline]
214
    pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
R
Ralf Jung 已提交
215 216 217
        self.to_mem_place().to_ptr()
    }
}
O
Oliver Schneider 已提交
218

R
Ralf Jung 已提交
219 220 221 222 223 224 225 226 227 228
impl<'tcx> PlaceTy<'tcx> {
    /// Produces a Place that will error if attempted to be read from or written to
    #[inline]
    pub fn null(cx: impl HasDataLayout, layout: TyLayout<'tcx>) -> Self {
        PlaceTy { place: Place::from_scalar_ptr(Scalar::ptr_null(cx), layout.align), layout }
    }

    #[inline]
    pub fn to_mem_place(self) -> MPlaceTy<'tcx> {
        MPlaceTy { mplace: self.place.to_mem_place(), layout: self.layout }
O
Oliver Schneider 已提交
229 230 231
    }
}

232
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
R
Ralf Jung 已提交
233 234 235 236 237 238 239
    /// Take a value, which represents a (thin or fat) reference, and make it a place.
    /// Alignment is just based on the type.  This is the inverse of `MemPlace::to_ref`.
    pub fn ref_to_mplace(
        &self, val: ValTy<'tcx>
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
        let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty;
        let layout = self.layout_of(pointee_type)?;
R
Ralf Jung 已提交
240 241 242 243 244 245
        let align = layout.align;
        let mplace = match *val {
            Value::Scalar(ptr) =>
                MemPlace { ptr: ptr.not_undef()?, align, extra: None },
            Value::ScalarPair(ptr, extra) =>
                MemPlace { ptr: ptr.not_undef()?, align, extra: Some(extra.not_undef()?) },
R
Ralf Jung 已提交
246 247 248 249 250 251 252 253 254
        };
        Ok(MPlaceTy { mplace, layout })
    }

    /// Offset a pointer to project to a field. Unlike place_field, this is always
    /// possible without allocating, so it can take &self. Also return the field's layout.
    /// This supports both struct and array fields.
    #[inline(always)]
    pub fn mplace_field(
O
Oliver Schneider 已提交
255
        &self,
R
Ralf Jung 已提交
256 257 258 259 260 261 262 263
        base: MPlaceTy<'tcx>,
        field: u64,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
        // Not using the layout method because we want to compute on u64
        let offset = match base.layout.fields {
            layout::FieldPlacement::Arbitrary { ref offsets, .. } =>
                offsets[usize::try_from(field).unwrap()],
            layout::FieldPlacement::Array { stride, .. } => {
264 265 266
                let len = base.len(self)?;
                assert!(field < len, "Tried to access element {} of array/slice with length {}",
                    field, len);
R
Ralf Jung 已提交
267 268
                stride * field
            }
269
            layout::FieldPlacement::Union(count) => {
B
Bernardo Meurer 已提交
270 271
                assert!(field < count as u64,
                        "Tried to access field {} of union with {} fields", field, count);
272 273 274
                // Offset is always 0
                Size::from_bytes(0)
            }
R
Ralf Jung 已提交
275 276 277
        };
        // the only way conversion can fail if is this is an array (otherwise we already panicked
        // above). In that case, all fields are equal.
R
Ralf Jung 已提交
278
        let field_layout = base.layout.field(self, usize::try_from(field).unwrap_or(0))?;
R
Ralf Jung 已提交
279

280 281 282 283 284
        // Offset may need adjustment for unsized fields
        let (extra, offset) = if field_layout.is_unsized() {
            // re-use parent metadata to determine dynamic field layout
            let (_, align) = self.size_and_align_of(base.extra, field_layout)?;
            (base.extra, offset.abi_align(align))
R
Ralf Jung 已提交
285 286

        } else {
287 288 289
            // base.extra could be present; we might be accessing a sized field of an unsized
            // struct.
            (None, offset)
R
Ralf Jung 已提交
290 291
        };

292
        let ptr = base.ptr.ptr_offset(offset, self)?;
293 294 295 296
        let align = base.align
            // We do not look at `base.layout.align` nor `field_layout.align`, unlike
            // codegen -- mostly to see if we can get away with that
            .restrict_for_offset(offset); // must be last thing that happens
297

R
Ralf Jung 已提交
298
        Ok(MPlaceTy { mplace: MemPlace { ptr, align, extra }, layout: field_layout })
O
Oliver Schneider 已提交
299 300
    }

301 302 303 304 305 306
    // Iterates over all fields of an array. Much more efficient than doing the
    // same by repeatedly calling `mplace_array`.
    pub fn mplace_array_fields(
        &self,
        base: MPlaceTy<'tcx>,
    ) -> EvalResult<'tcx, impl Iterator<Item=EvalResult<'tcx, MPlaceTy<'tcx>>> + 'a> {
307
        let len = base.len(self)?; // also asserts that we have a type where this makes sense
308 309 310 311 312 313 314 315 316
        let stride = match base.layout.fields {
            layout::FieldPlacement::Array { stride, .. } => stride,
            _ => bug!("mplace_array_fields: expected an array layout"),
        };
        let layout = base.layout.field(self, 0)?;
        let dl = &self.tcx.data_layout;
        Ok((0..len).map(move |i| {
            let ptr = base.ptr.ptr_offset(i * stride, dl)?;
            Ok(MPlaceTy {
317
                mplace: MemPlace { ptr, align: base.align, extra: None },
318 319 320 321 322
                layout
            })
        }))
    }

R
Ralf Jung 已提交
323
    pub fn mplace_subslice(
O
Oliver Schneider 已提交
324
        &self,
R
Ralf Jung 已提交
325 326 327 328
        base: MPlaceTy<'tcx>,
        from: u64,
        to: u64,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
329
        let len = base.len(self)?; // also asserts that we have a type where this makes sense
R
Ralf Jung 已提交
330 331 332 333 334 335 336 337
        assert!(from <= len - to);

        // Not using layout method because that works with usize, and does not work with slices
        // (that have count 0 in their layout).
        let from_offset = match base.layout.fields {
            layout::FieldPlacement::Array { stride, .. } =>
                stride * from,
            _ => bug!("Unexpected layout of index access: {:#?}", base.layout),
O
Oliver Schneider 已提交
338
        };
R
Ralf Jung 已提交
339 340 341 342 343
        let ptr = base.ptr.ptr_offset(from_offset, self)?;

        // Compute extra and new layout
        let inner_len = len - to - from;
        let (extra, ty) = match base.layout.ty.sty {
R
Ralf Jung 已提交
344 345
            // It is not nice to match on the type, but that seems to be the only way to
            // implement this.
V
varkor 已提交
346
            ty::Array(inner, _) =>
347 348
                (None, self.tcx.mk_array(inner, inner_len)),
            ty::Slice(..) => {
349
                let len = Scalar::from_uint(inner_len, self.pointer_size());
350 351
                (Some(len), base.layout.ty)
            }
R
Ralf Jung 已提交
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
            _ =>
                bug!("cannot subslice non-array type: `{:?}`", base.layout.ty),
        };
        let layout = self.layout_of(ty)?;

        Ok(MPlaceTy {
            mplace: MemPlace { ptr, align: base.align, extra },
            layout
        })
    }

    pub fn mplace_downcast(
        &self,
        base: MPlaceTy<'tcx>,
        variant: usize,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
        // Downcasts only change the layout
369
        assert_eq!(base.extra, None);
R
Ralf Jung 已提交
370
        Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base })
O
Oliver Schneider 已提交
371 372
    }

R
Ralf Jung 已提交
373 374
    /// Project into an mplace
    pub fn mplace_projection(
O
Oliver Schneider 已提交
375
        &self,
R
Ralf Jung 已提交
376 377 378
        base: MPlaceTy<'tcx>,
        proj_elem: &mir::PlaceElem<'tcx>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
379
        use rustc::mir::ProjectionElem::*;
R
Ralf Jung 已提交
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
        Ok(match *proj_elem {
            Field(field, _) => self.mplace_field(base, field.index() as u64)?,
            Downcast(_, variant) => self.mplace_downcast(base, variant)?,
            Deref => self.deref_operand(base.into())?,

            Index(local) => {
                let n = *self.frame().locals[local].access()?;
                let n_layout = self.layout_of(self.tcx.types.usize)?;
                let n = self.read_scalar(OpTy { op: n, layout: n_layout })?;
                let n = n.to_bits(self.tcx.data_layout.pointer_size)?;
                self.mplace_field(base, u64::try_from(n).unwrap())?
            }

            ConstantIndex {
                offset,
                min_length,
                from_end,
            } => {
398
                let n = base.len(self)?;
R
Ralf Jung 已提交
399 400 401 402 403 404 405 406 407 408 409 410 411 412
                assert!(n >= min_length as u64);

                let index = if from_end {
                    n - u64::from(offset)
                } else {
                    u64::from(offset)
                };

                self.mplace_field(base, index)?
            }

            Subslice { from, to } =>
                self.mplace_subslice(base, u64::from(from), u64::from(to))?,
        })
O
Oliver Schneider 已提交
413 414
    }

R
Ralf Jung 已提交
415 416 417
    /// Get the place of a field inside the place, and also the field's type.
    /// Just a convenience function, but used quite a bit.
    pub fn place_field(
O
Oliver Schneider 已提交
418
        &mut self,
R
Ralf Jung 已提交
419
        base: PlaceTy<'tcx>,
R
Ralf Jung 已提交
420 421 422 423 424 425
        field: u64,
    ) -> EvalResult<'tcx, PlaceTy<'tcx>> {
        // FIXME: We could try to be smarter and avoid allocation for fields that span the
        // entire place.
        let mplace = self.force_allocation(base)?;
        Ok(self.mplace_field(mplace, field)?.into())
O
Oliver Schneider 已提交
426 427
    }

R
Ralf Jung 已提交
428 429
    pub fn place_downcast(
        &mut self,
R
Ralf Jung 已提交
430
        base: PlaceTy<'tcx>,
R
Ralf Jung 已提交
431 432 433 434 435 436 437 438 439
        variant: usize,
    ) -> EvalResult<'tcx, PlaceTy<'tcx>> {
        // Downcast just changes the layout
        Ok(match base.place {
            Place::Ptr(mplace) =>
                self.mplace_downcast(MPlaceTy { mplace, layout: base.layout }, variant)?.into(),
            Place::Local { .. } => {
                let layout = base.layout.for_variant(&self, variant);
                PlaceTy { layout, ..base }
O
Oliver Schneider 已提交
440
            }
R
Ralf Jung 已提交
441
        })
O
Oliver Schneider 已提交
442 443
    }

R
Ralf Jung 已提交
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
    /// Project into a place
    pub fn place_projection(
        &mut self,
        base: PlaceTy<'tcx>,
        proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>,
    ) -> EvalResult<'tcx, PlaceTy<'tcx>> {
        use rustc::mir::ProjectionElem::*;
        Ok(match *proj_elem {
            Field(field, _) =>  self.place_field(base, field.index() as u64)?,
            Downcast(_, variant) => self.place_downcast(base, variant)?,
            Deref => self.deref_operand(self.place_to_op(base)?)?.into(),
            // For the other variants, we have to force an allocation.
            // This matches `operand_projection`.
            Subslice { .. } | ConstantIndex { .. } | Index(_) => {
                let mplace = self.force_allocation(base)?;
                self.mplace_projection(mplace, proj_elem)?.into()
            }
        })
    }

464 465 466 467 468 469
    /// Evaluate statics and promoteds to an `MPlace`.  Used to share some code between
    /// `eval_place` and `eval_place_to_op`.
    pub(super) fn eval_place_to_mplace(
        &self,
        mir_place: &mir::Place<'tcx>
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
470
        use rustc::mir::Place::*;
471
        Ok(match *mir_place {
472 473
            Promoted(ref promoted) => {
                let instance = self.frame().instance;
R
Ralf Jung 已提交
474
                let op = self.global_to_op(GlobalId {
475 476 477
                    instance,
                    promoted: Some(promoted.0),
                })?;
478
                let mplace = op.to_mem_place(); // these are always in memory
R
Ralf Jung 已提交
479
                let ty = self.monomorphize(promoted.1, self.substs());
480 481
                MPlaceTy {
                    mplace,
R
Ralf Jung 已提交
482
                    layout: self.layout_of(ty)?,
483 484 485
                }
            }

O
Oliver Schneider 已提交
486
            Static(ref static_) => {
R
Ralf Jung 已提交
487 488
                let ty = self.monomorphize(static_.ty, self.substs());
                let layout = self.layout_of(ty)?;
489 490 491 492 493
                let instance = ty::Instance::mono(*self.tcx, static_.def_id);
                let cid = GlobalId {
                    instance,
                    promoted: None
                };
R
Ralf Jung 已提交
494
                // Just create a lazy reference, so we can support recursive statics.
R
Ralf Jung 已提交
495
                // tcx takes are of assigning every static one and only one unique AllocId.
R
Ralf Jung 已提交
496 497
                // When the data here is ever actually used, memory will notice,
                // and it knows how to deal with alloc_id that are present in the
R
Ralf Jung 已提交
498 499 500 501 502 503 504 505
                // global table but not in its local memory: It calls back into tcx through
                // a query, triggering the CTFE machinery to actually turn this lazy reference
                // into a bunch of bytes.  IOW, statics are evaluated with CTFE even when
                // this EvalContext uses another Machine (e.g., in miri).  This is what we
                // want!  This way, computing statics works concistently between codegen
                // and miri: They use the same query to eventually obtain a `ty::Const`
                // and use that for further computation.
                let alloc = self.tcx.alloc_map.lock().intern_static(cid.instance.def_id());
506
                MPlaceTy::from_aligned_ptr(alloc.into(), layout)
O
Oliver Schneider 已提交
507 508
            }

509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
            _ => bug!("eval_place_to_mplace called on {:?}", mir_place),
        })
    }

    /// Compute a place.  You should only use this if you intend to write into this
    /// place; for reading, a more efficient alternative is `eval_place_for_read`.
    pub fn eval_place(&mut self, mir_place: &mir::Place<'tcx>) -> EvalResult<'tcx, PlaceTy<'tcx>> {
        use rustc::mir::Place::*;
        let place = match *mir_place {
            Local(mir::RETURN_PLACE) => PlaceTy {
                place: self.frame().return_place,
                layout: self.layout_of_local(self.cur_frame(), mir::RETURN_PLACE)?,
            },
            Local(local) => PlaceTy {
                place: Place::Local {
                    frame: self.cur_frame(),
                    local,
                },
                layout: self.layout_of_local(self.cur_frame(), local)?,
            },

O
Oliver Schneider 已提交
530 531
            Projection(ref proj) => {
                let place = self.eval_place(&proj.base)?;
R
Ralf Jung 已提交
532
                self.place_projection(place, &proj.elem)?
O
Oliver Schneider 已提交
533
            }
534 535

            _ => self.eval_place_to_mplace(mir_place)?.into(),
O
Oliver Schneider 已提交
536 537
        };

R
Ralf Jung 已提交
538
        self.dump_place(place.place);
O
Oliver Schneider 已提交
539 540 541
        Ok(place)
    }

R
Ralf Jung 已提交
542 543
    /// Write a scalar to a place
    pub fn write_scalar(
O
Oliver Schneider 已提交
544
        &mut self,
R
Ralf Jung 已提交
545 546 547 548 549
        val: impl Into<ScalarMaybeUndef>,
        dest: PlaceTy<'tcx>,
    ) -> EvalResult<'tcx> {
        self.write_value(Value::Scalar(val.into()), dest)
    }
O
Oliver Schneider 已提交
550

R
Ralf Jung 已提交
551 552 553 554
    /// Write a value to a place
    pub fn write_value(
        &mut self,
        src_val: Value,
R
Ralf Jung 已提交
555
        dest: PlaceTy<'tcx>,
R
Ralf Jung 已提交
556
    ) -> EvalResult<'tcx> {
R
Ralf Jung 已提交
557
        trace!("write_value: {:?} <- {:?}", *dest, src_val);
R
Ralf Jung 已提交
558 559
        // See if we can avoid an allocation. This is the counterpart to `try_read_value`,
        // but not factored as a separate function.
R
Ralf Jung 已提交
560
        let mplace = match dest.place {
O
Oliver Schneider 已提交
561
            Place::Local { frame, local } => {
R
Ralf Jung 已提交
562 563 564 565 566
                match *self.stack[frame].locals[local].access_mut()? {
                    Operand::Immediate(ref mut dest_val) => {
                        // Yay, we can just change the local directly.
                        *dest_val = src_val;
                        return Ok(());
567
                    },
R
Ralf Jung 已提交
568
                    Operand::Indirect(mplace) => mplace, // already in memory
O
Oliver Schneider 已提交
569
                }
R
Ralf Jung 已提交
570
            },
R
Ralf Jung 已提交
571
            Place::Ptr(mplace) => mplace, // already in memory
O
Oliver Schneider 已提交
572 573
        };

R
Ralf Jung 已提交
574 575
        // This is already in memory, write there.
        let dest = MPlaceTy { mplace, layout: dest.layout };
R
Ralf Jung 已提交
576
        self.write_value_to_mplace(src_val, dest)
O
Oliver Schneider 已提交
577 578
    }

R
Ralf Jung 已提交
579 580 581 582 583 584
    /// Write a value to memory
    fn write_value_to_mplace(
        &mut self,
        value: Value,
        dest: MPlaceTy<'tcx>,
    ) -> EvalResult<'tcx> {
585
        let (ptr, ptr_align) = dest.to_scalar_ptr_align();
B
Bernardo Meurer 已提交
586 587 588 589
        // Note that it is really important that the type here is the right one, and matches the
        // type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here
        // to handle padding properly, which is only correct if we never look at this data with the
        // wrong type.
590 591

        // Nothing to do for ZSTs, other than checking alignment
592
        if dest.layout.is_zst() {
593 594 595 596 597
            self.memory.check_align(ptr, ptr_align)?;
            return Ok(());
        }

        let ptr = ptr.to_ptr()?;
R
Ralf Jung 已提交
598 599 600
        match value {
            Value::Scalar(scalar) => {
                self.memory.write_scalar(
601
                    ptr, ptr_align.min(dest.layout.align), scalar, dest.layout.size
R
Ralf Jung 已提交
602
                )
O
Oliver Schneider 已提交
603
            }
R
Ralf Jung 已提交
604 605 606
            Value::ScalarPair(a_val, b_val) => {
                let (a, b) = match dest.layout.abi {
                    layout::Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value),
B
Bernardo Meurer 已提交
607 608
                    _ => bug!("write_value_to_mplace: invalid ScalarPair layout: {:#?}",
                              dest.layout)
R
Ralf Jung 已提交
609 610 611 612
                };
                let (a_size, b_size) = (a.size(&self), b.size(&self));
                let (a_align, b_align) = (a.align(&self), b.align(&self));
                let b_offset = a_size.abi_align(b_align);
613
                let b_ptr = ptr.offset(b_offset, &self)?.into();
614

615 616
                self.memory.write_scalar(ptr, ptr_align.min(a_align), a_val, a_size)?;
                self.memory.write_scalar(b_ptr, ptr_align.min(b_align), b_val, b_size)
O
Oliver Schneider 已提交
617
            }
R
Ralf Jung 已提交
618
        }
O
Oliver Schneider 已提交
619 620
    }

R
Ralf Jung 已提交
621 622
    /// Copy the data from an operand to a place
    pub fn copy_op(
O
Oliver Schneider 已提交
623
        &mut self,
R
Ralf Jung 已提交
624 625 626
        src: OpTy<'tcx>,
        dest: PlaceTy<'tcx>,
    ) -> EvalResult<'tcx> {
627 628
        assert!(!src.layout.is_unsized() && !dest.layout.is_unsized(),
            "Cannot copy unsized data");
R
Ralf Jung 已提交
629 630
        assert_eq!(src.layout.size, dest.layout.size,
            "Size mismatch when copying!\nsrc: {:#?}\ndest: {:#?}", src, dest);
R
Ralf Jung 已提交
631 632 633 634

        // Let us see if the layout is simple so we take a shortcut, avoid force_allocation.
        let (src_ptr, src_align) = match self.try_read_value(src)? {
            Ok(src_val) =>
R
Ralf Jung 已提交
635 636 637 638
                // Yay, we got a value that we can write directly.  We write with the
                // *source layout*, because that was used to load, and if they do not match
                // this is a transmute we want to support.
                return self.write_value(src_val, PlaceTy { place: *dest, layout: src.layout }),
R
Ralf Jung 已提交
639 640 641
            Err(mplace) => mplace.to_scalar_ptr_align(),
        };
        // Slow path, this does not fit into an immediate. Just memcpy.
R
Ralf Jung 已提交
642
        trace!("copy_op: {:?} <- {:?}", *dest, *src);
R
Ralf Jung 已提交
643 644 645 646 647 648
        let (dest_ptr, dest_align) = self.force_allocation(dest)?.to_scalar_ptr_align();
        self.memory.copy(
            src_ptr, src_align,
            dest_ptr, dest_align,
            src.layout.size, false
        )
O
Oliver Schneider 已提交
649 650
    }

R
Ralf Jung 已提交
651 652 653
    /// Make sure that a place is in memory, and return where it is.
    /// This is essentially `force_to_memplace`.
    pub fn force_allocation(
O
Oliver Schneider 已提交
654
        &mut self,
R
Ralf Jung 已提交
655 656 657 658
        place: PlaceTy<'tcx>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
        let mplace = match place.place {
            Place::Local { frame, local } => {
659 660 661 662 663 664 665 666
                match *self.stack[frame].locals[local].access()? {
                    Operand::Indirect(mplace) => mplace,
                    Operand::Immediate(value) => {
                        // We need to make an allocation.
                        // FIXME: Consider not doing anything for a ZST, and just returning
                        // a fake pointer?  Are we even called for ZST?

                        // We need the layout of the local.  We can NOT use the layout we got,
R
Ralf Jung 已提交
667 668
                        // that might e.g. be an inner field of a struct with `Scalar` layout,
                        // that has different alignment than the outer field.
669 670 671 672 673 674 675 676 677 678
                        let local_layout = self.layout_of_local(frame, local)?;
                        let ptr = self.allocate(local_layout, MemoryKind::Stack)?;
                        self.write_value_to_mplace(value, ptr)?;
                        let mplace = ptr.mplace;
                        // Update the local
                        *self.stack[frame].locals[local].access_mut()? =
                            Operand::Indirect(mplace);
                        mplace
                    }
                }
R
Ralf Jung 已提交
679 680 681 682 683
            }
            Place::Ptr(mplace) => mplace
        };
        // Return with the original layout, so that the caller can go on
        Ok(MPlaceTy { mplace, layout: place.layout })
O
Oliver Schneider 已提交
684 685
    }

R
Ralf Jung 已提交
686
    pub fn allocate(
O
Oliver Schneider 已提交
687
        &mut self,
R
Ralf Jung 已提交
688 689 690 691 692 693 694
        layout: TyLayout<'tcx>,
        kind: MemoryKind<M::MemoryKinds>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
        assert!(!layout.is_unsized(), "cannot alloc memory for unsized type");
        let ptr = self.memory.allocate(layout.size, layout.align, kind)?;
        Ok(MPlaceTy::from_aligned_ptr(ptr, layout))
    }
O
Oliver Schneider 已提交
695

696
    pub fn write_discriminant_index(
R
Ralf Jung 已提交
697 698 699 700 701 702
        &mut self,
        variant_index: usize,
        dest: PlaceTy<'tcx>,
    ) -> EvalResult<'tcx> {
        match dest.layout.variants {
            layout::Variants::Single { index } => {
R
Ralf Jung 已提交
703
                assert_eq!(index, variant_index);
O
Oliver Schneider 已提交
704
            }
R
Ralf Jung 已提交
705
            layout::Variants::Tagged { ref tag, .. } => {
706
                let adt_def = dest.layout.ty.ty_adt_def().unwrap();
R
Ralf Jung 已提交
707
                assert!(variant_index < adt_def.variants.len());
708
                let discr_val = adt_def
R
Ralf Jung 已提交
709 710 711 712 713 714 715 716 717 718 719
                    .discriminant_for_variant(*self.tcx, variant_index)
                    .val;

                // raw discriminants for enums are isize or bigger during
                // their computation, but the in-memory tag is the smallest possible
                // representation
                let size = tag.value.size(self.tcx.tcx);
                let shift = 128 - size.bits();
                let discr_val = (discr_val << shift) >> shift;

                let discr_dest = self.place_field(dest, 0)?;
720
                self.write_scalar(Scalar::from_uint(discr_val, size), discr_dest)?;
O
Oliver Schneider 已提交
721
            }
R
Ralf Jung 已提交
722 723 724 725 726
            layout::Variants::NicheFilling {
                dataful_variant,
                ref niche_variants,
                niche_start,
                ..
O
Oliver Schneider 已提交
727
            } => {
R
Ralf Jung 已提交
728
                assert!(variant_index < dest.layout.ty.ty_adt_def().unwrap().variants.len());
R
Ralf Jung 已提交
729 730 731 732 733
                if variant_index != dataful_variant {
                    let niche_dest =
                        self.place_field(dest, 0)?;
                    let niche_value = ((variant_index - niche_variants.start()) as u128)
                        .wrapping_add(niche_start);
734 735 736 737
                    self.write_scalar(
                        Scalar::from_uint(niche_value, niche_dest.layout.size),
                        niche_dest
                    )?;
R
Ralf Jung 已提交
738
                }
O
Oliver Schneider 已提交
739
            }
740
        }
R
Ralf Jung 已提交
741 742

        Ok(())
O
Oliver Schneider 已提交
743 744
    }

R
Ralf Jung 已提交
745
    /// Every place can be read from, so we can turm them into an operand
746
    #[inline(always)]
R
Ralf Jung 已提交
747 748 749 750 751 752 753 754 755
    pub fn place_to_op(&self, place: PlaceTy<'tcx>) -> EvalResult<'tcx, OpTy<'tcx>> {
        let op = match place.place {
            Place::Ptr(mplace) => {
                Operand::Indirect(mplace)
            }
            Place::Local { frame, local } =>
                *self.stack[frame].locals[local].access()?
        };
        Ok(OpTy { op, layout: place.layout })
O
Oliver Schneider 已提交
756
    }
757

758 759 760 761 762 763 764 765 766
    /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
    /// Also return some more information so drop doesn't have to run the same code twice.
    pub(super) fn unpack_dyn_trait(&self, mplace: MPlaceTy<'tcx>)
    -> EvalResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx>)> {
        let vtable = mplace.vtable()?; // also sanity checks the type
        let (instance, ty) = self.read_drop_type_from_vtable(vtable)?;
        let layout = self.layout_of(ty)?;

        // More sanity checks
767 768 769 770 771
        if cfg!(debug_assertions) {
            let (size, align) = self.read_size_and_align_from_vtable(vtable)?;
            assert_eq!(size, layout.size);
            assert_eq!(align.abi(), layout.align.abi()); // only ABI alignment is preserved
        }
772 773 774

        let mplace = MPlaceTy {
            mplace: MemPlace { extra: None, ..*mplace },
775
            layout
776 777
        };
        Ok((instance, mplace))
778
    }
O
Oliver Schneider 已提交
779
}