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 21
use rustc_data_structures::indexed_vec::Idx;

R
Ralf Jung 已提交
22 23 24 25 26 27 28 29 30 31 32 33
use rustc::mir::interpret::{
    GlobalId, Scalar, EvalResult, Pointer, ScalarMaybeUndef
};
use super::{EvalContext, Machine, Value, ValTy, Operand, OpTy, MemoryKind};

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct MemPlace {
    /// 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.
    pub ptr: Scalar,
    pub align: Align,
34
    /// Metadata for unsized places.  Interpretation is up to the type.
R
Ralf Jung 已提交
35 36
    /// Must not be present for sized types, but can be missing for unsized types
    /// (e.g. `extern type`).
37
    pub extra: Option<Scalar>,
R
Ralf Jung 已提交
38
}
O
Oliver Schneider 已提交
39

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

R
Ralf Jung 已提交
45 46 47 48 49 50
    /// 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 已提交
51 52
}

R
Ralf Jung 已提交
53 54 55 56 57 58 59 60
#[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;
61
    #[inline(always)]
R
Ralf Jung 已提交
62 63 64 65 66 67 68 69 70 71 72 73 74 75
    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;
76
    #[inline(always)]
R
Ralf Jung 已提交
77 78
    fn deref(&self) -> &MemPlace {
        &self.mplace
O
Oliver Schneider 已提交
79
    }
R
Ralf Jung 已提交
80 81 82
}

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

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

R
Ralf Jung 已提交
102 103 104 105 106 107 108
    #[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) {
109
        assert_eq!(self.extra, None);
R
Ralf Jung 已提交
110 111 112 113 114 115
        (self.ptr, self.align)
    }

    /// Extract the ptr part of the mplace
    #[inline(always)]
    pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
B
Bernardo Meurer 已提交
116 117 118
        // 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 已提交
119 120 121 122 123
        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`.
124
    pub fn to_ref(self) -> Value {
R
Ralf Jung 已提交
125 126 127
        // We ignore the alignment of the place here -- special handling for packed structs ends
        // at the `&` operator.
        match self.extra {
128 129
            None => Value::Scalar(self.ptr.into()),
            Some(extra) => Value::ScalarPair(self.ptr.into(), extra.into()),
R
Ralf Jung 已提交
130 131 132 133 134 135 136 137 138 139 140
        }
    }
}

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]
141
    pub(super) fn len(self, cx: impl HasDataLayout) -> EvalResult<'tcx, u64> {
142 143 144 145 146 147
        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),
148
            }
149 150 151 152 153 154
        } 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),
155 156 157 158 159 160 161 162 163
            }
        }
    }

    #[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 已提交
164 165 166 167 168
        }
    }
}

impl<'tcx> OpTy<'tcx> {
169
    #[inline(always)]
R
Ralf Jung 已提交
170 171 172 173 174 175 176
    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),
        }
    }

177
    #[inline(always)]
R
Ralf Jung 已提交
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
    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]
196
    pub fn from_ptr(ptr: Pointer, align: Align) -> Self {
R
Ralf Jung 已提交
197
        Place::Ptr(MemPlace::from_ptr(ptr, align))
O
Oliver Schneider 已提交
198 199
    }

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

        }
    }

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

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

R
Ralf Jung 已提交
220 221 222 223 224 225 226 227 228 229
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 已提交
230 231 232
    }
}

O
Oliver Schneider 已提交
233
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
R
Ralf Jung 已提交
234 235 236 237 238 239 240
    /// 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 已提交
241 242 243 244 245 246
        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 已提交
247 248 249 250 251 252 253 254 255
        };
        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 已提交
256
        &self,
R
Ralf Jung 已提交
257 258 259 260 261 262 263 264
        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, .. } => {
265 266 267
                let len = base.len(self)?;
                assert!(field < len, "Tried to access element {} of array/slice with length {}",
                    field, len);
R
Ralf Jung 已提交
268 269
                stride * field
            }
270
            layout::FieldPlacement::Union(count) => {
B
Bernardo Meurer 已提交
271 272
                assert!(field < count as u64,
                        "Tried to access field {} of union with {} fields", field, count);
273 274 275
                // Offset is always 0
                Size::from_bytes(0)
            }
R
Ralf Jung 已提交
276 277 278
        };
        // 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 已提交
279
        let field_layout = base.layout.field(self, usize::try_from(field).unwrap_or(0))?;
R
Ralf Jung 已提交
280

281 282 283 284 285
        // 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 已提交
286 287

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

293 294 295
        let ptr = base.ptr.ptr_offset(offset, self)?;
        let align = base.align.min(field_layout.align); // only use static information

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

299 300 301 302 303 304
    // 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> {
305
        let len = base.len(self)?; // also asserts that we have a type where this makes sense
306 307 308 309 310 311 312 313 314
        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 {
315
                mplace: MemPlace { ptr, align: base.align, extra: None },
316 317 318 319 320
                layout
            })
        }))
    }

R
Ralf Jung 已提交
321
    pub fn mplace_subslice(
O
Oliver Schneider 已提交
322
        &self,
R
Ralf Jung 已提交
323 324 325 326
        base: MPlaceTy<'tcx>,
        from: u64,
        to: u64,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
327
        let len = base.len(self)?; // also asserts that we have a type where this makes sense
R
Ralf Jung 已提交
328 329 330 331 332 333 334 335
        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 已提交
336
        };
R
Ralf Jung 已提交
337 338 339 340 341
        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 已提交
342 343
            // It is not nice to match on the type, but that seems to be the only way to
            // implement this.
V
varkor 已提交
344
            ty::Array(inner, _) =>
345 346 347 348 349 350 351 352
                (None, self.tcx.mk_array(inner, inner_len)),
            ty::Slice(..) => {
                let len = Scalar::Bits {
                    bits: inner_len.into(),
                    size: self.memory.pointer_size().bytes() as u8
                };
                (Some(len), base.layout.ty)
            }
R
Ralf Jung 已提交
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
            _ =>
                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
370
        assert_eq!(base.extra, None);
R
Ralf Jung 已提交
371
        Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base })
O
Oliver Schneider 已提交
372 373
    }

R
Ralf Jung 已提交
374 375
    /// Project into an mplace
    pub fn mplace_projection(
O
Oliver Schneider 已提交
376
        &self,
R
Ralf Jung 已提交
377 378 379
        base: MPlaceTy<'tcx>,
        proj_elem: &mir::PlaceElem<'tcx>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
380
        use rustc::mir::ProjectionElem::*;
R
Ralf Jung 已提交
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
        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,
            } => {
399
                let n = base.len(self)?;
R
Ralf Jung 已提交
400 401 402 403 404 405 406 407 408 409 410 411 412 413
                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 已提交
414 415
    }

R
Ralf Jung 已提交
416 417 418
    /// 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 已提交
419
        &mut self,
R
Ralf Jung 已提交
420
        base: PlaceTy<'tcx>,
R
Ralf Jung 已提交
421 422 423 424 425 426
        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 已提交
427 428
    }

R
Ralf Jung 已提交
429 430
    pub fn place_downcast(
        &mut self,
R
Ralf Jung 已提交
431
        base: PlaceTy<'tcx>,
R
Ralf Jung 已提交
432 433 434 435 436 437 438 439 440
        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 已提交
441
            }
R
Ralf Jung 已提交
442
        })
O
Oliver Schneider 已提交
443 444
    }

R
Ralf Jung 已提交
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
    /// 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()
            }
        })
    }

465 466 467 468 469 470
    /// 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>> {
471
        use rustc::mir::Place::*;
472
        Ok(match *mir_place {
473 474
            Promoted(ref promoted) => {
                let instance = self.frame().instance;
R
Ralf Jung 已提交
475
                let op = self.global_to_op(GlobalId {
476 477 478
                    instance,
                    promoted: Some(promoted.0),
                })?;
479
                let mplace = op.to_mem_place(); // these are always in memory
R
Ralf Jung 已提交
480
                let ty = self.monomorphize(promoted.1, self.substs());
481 482
                MPlaceTy {
                    mplace,
R
Ralf Jung 已提交
483
                    layout: self.layout_of(ty)?,
484 485 486
                }
            }

O
Oliver Schneider 已提交
487
            Static(ref static_) => {
R
Ralf Jung 已提交
488 489
                let ty = self.monomorphize(static_.ty, self.substs());
                let layout = self.layout_of(ty)?;
490 491 492 493 494
                let instance = ty::Instance::mono(*self.tcx, static_.def_id);
                let cid = GlobalId {
                    instance,
                    promoted: None
                };
R
Ralf Jung 已提交
495
                // Just create a lazy reference, so we can support recursive statics.
R
Ralf Jung 已提交
496
                // tcx takes are of assigning every static one and only one unique AllocId.
R
Ralf Jung 已提交
497 498
                // 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 已提交
499 500 501 502 503 504 505 506
                // 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());
507
                MPlaceTy::from_aligned_ptr(alloc.into(), layout)
O
Oliver Schneider 已提交
508 509
            }

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
            _ => 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 已提交
531 532
            Projection(ref proj) => {
                let place = self.eval_place(&proj.base)?;
R
Ralf Jung 已提交
533
                self.place_projection(place, &proj.elem)?
O
Oliver Schneider 已提交
534
            }
535 536

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

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

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

R
Ralf Jung 已提交
552 553 554 555
    /// Write a value to a place
    pub fn write_value(
        &mut self,
        src_val: Value,
R
Ralf Jung 已提交
556
        dest: PlaceTy<'tcx>,
R
Ralf Jung 已提交
557
    ) -> EvalResult<'tcx> {
R
Ralf Jung 已提交
558
        trace!("write_value: {:?} <- {:?}", *dest, src_val);
R
Ralf Jung 已提交
559 560
        // 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 已提交
561
        let mplace = match dest.place {
O
Oliver Schneider 已提交
562
            Place::Local { frame, local } => {
R
Ralf Jung 已提交
563 564 565 566 567
                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(());
568
                    },
R
Ralf Jung 已提交
569
                    Operand::Indirect(mplace) => mplace, // already in memory
O
Oliver Schneider 已提交
570
                }
R
Ralf Jung 已提交
571
            },
R
Ralf Jung 已提交
572
            Place::Ptr(mplace) => mplace, // already in memory
O
Oliver Schneider 已提交
573 574
        };

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

R
Ralf Jung 已提交
580 581 582 583 584 585
    /// Write a value to memory
    fn write_value_to_mplace(
        &mut self,
        value: Value,
        dest: MPlaceTy<'tcx>,
    ) -> EvalResult<'tcx> {
586
        let (ptr, ptr_align) = dest.to_scalar_ptr_align();
B
Bernardo Meurer 已提交
587 588 589 590
        // 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.
591 592 593 594 595 596 597 598

        // Nothing to do for ZSTs, other than checking alignment
        if dest.layout.size.bytes() == 0 {
            self.memory.check_align(ptr, ptr_align)?;
            return Ok(());
        }

        let ptr = ptr.to_ptr()?;
R
Ralf Jung 已提交
599 600 601
        match value {
            Value::Scalar(scalar) => {
                self.memory.write_scalar(
602
                    ptr, ptr_align.min(dest.layout.align), scalar, dest.layout.size
R
Ralf Jung 已提交
603
                )
O
Oliver Schneider 已提交
604
            }
R
Ralf Jung 已提交
605 606 607
            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 已提交
608 609
                    _ => bug!("write_value_to_mplace: invalid ScalarPair layout: {:#?}",
                              dest.layout)
R
Ralf Jung 已提交
610 611 612 613
                };
                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);
614
                let b_ptr = ptr.offset(b_offset, &self)?.into();
615

616 617
                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 已提交
618
            }
R
Ralf Jung 已提交
619
        }
O
Oliver Schneider 已提交
620 621
    }

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

        // 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 已提交
634 635 636 637
                // 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 已提交
638 639 640
            Err(mplace) => mplace.to_scalar_ptr_align(),
        };
        // Slow path, this does not fit into an immediate. Just memcpy.
R
Ralf Jung 已提交
641
        trace!("copy_op: {:?} <- {:?}", *dest, *src);
R
Ralf Jung 已提交
642 643 644 645 646 647
        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 已提交
648 649
    }

R
Ralf Jung 已提交
650 651 652
    /// 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 已提交
653
        &mut self,
R
Ralf Jung 已提交
654 655 656 657
        place: PlaceTy<'tcx>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
        let mplace = match place.place {
            Place::Local { frame, local } => {
658 659 660 661 662 663 664 665
                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 已提交
666 667
                        // that might e.g. be an inner field of a struct with `Scalar` layout,
                        // that has different alignment than the outer field.
668 669 670 671 672 673 674 675 676 677
                        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 已提交
678 679 680 681 682
            }
            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 已提交
683 684
    }

R
Ralf Jung 已提交
685
    pub fn allocate(
O
Oliver Schneider 已提交
686
        &mut self,
R
Ralf Jung 已提交
687 688 689 690 691 692 693
        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 已提交
694

695
    pub fn write_discriminant_index(
R
Ralf Jung 已提交
696 697 698 699 700 701
        &mut self,
        variant_index: usize,
        dest: PlaceTy<'tcx>,
    ) -> EvalResult<'tcx> {
        match dest.layout.variants {
            layout::Variants::Single { index } => {
R
Ralf Jung 已提交
702
                assert_eq!(index, variant_index);
O
Oliver Schneider 已提交
703
            }
R
Ralf Jung 已提交
704
            layout::Variants::Tagged { ref tag, .. } => {
705
                let adt_def = dest.layout.ty.ty_adt_def().unwrap();
R
Ralf Jung 已提交
706
                assert!(variant_index < adt_def.variants.len());
707
                let discr_val = adt_def
R
Ralf Jung 已提交
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
                    .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)?;
                self.write_scalar(Scalar::Bits {
                    bits: discr_val,
                    size: size.bytes() as u8,
                }, discr_dest)?;
O
Oliver Schneider 已提交
723
            }
R
Ralf Jung 已提交
724 725 726 727 728
            layout::Variants::NicheFilling {
                dataful_variant,
                ref niche_variants,
                niche_start,
                ..
O
Oliver Schneider 已提交
729
            } => {
R
Ralf Jung 已提交
730
                assert!(variant_index < dest.layout.ty.ty_adt_def().unwrap().variants.len());
R
Ralf Jung 已提交
731 732 733 734 735 736 737 738 739 740
                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);
                    self.write_scalar(Scalar::Bits {
                        bits: niche_value,
                        size: niche_dest.layout.size.bytes() as u8,
                    }, niche_dest)?;
                }
O
Oliver Schneider 已提交
741
            }
742
        }
R
Ralf Jung 已提交
743 744

        Ok(())
O
Oliver Schneider 已提交
745 746
    }

R
Ralf Jung 已提交
747
    /// Every place can be read from, so we can turm them into an operand
748
    #[inline(always)]
R
Ralf Jung 已提交
749 750 751 752 753 754 755 756 757
    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 已提交
758
    }
759

760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
    /// 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
        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
        // FIXME: More checks for the vtable? We could make sure it is exactly
        // the one one would expect for this type.

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