place.rs 29.1 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 35
    /// Metadata for unsized places.  Interpretation is up to the type.
    pub extra: Option<Scalar>,
R
Ralf Jung 已提交
36
}
O
Oliver Schneider 已提交
37

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

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

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

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

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

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

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

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

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

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

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

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

        }
    }

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

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

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

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

278 279 280 281 282
        // 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 已提交
283 284

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

290 291 292
        let ptr = base.ptr.ptr_offset(offset, self)?;
        let align = base.align.min(field_layout.align); // only use static information

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

613 614
                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 已提交
615
            }
R
Ralf Jung 已提交
616
        }
O
Oliver Schneider 已提交
617 618
    }

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

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

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

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

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

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

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

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
    /// 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 },
774
            layout
775 776
        };
        Ok((instance, mplace))
777
    }
O
Oliver Schneider 已提交
778
}