place.rs 29.8 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::ich::StableHashingContext;
18
use rustc::mir;
R
Ralf Jung 已提交
19
use rustc::ty::{self, Ty};
20
use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout};
21
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
O
Oliver Schneider 已提交
22

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

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

41 42 43 44 45 46
impl_stable_hash_for!(struct ::interpret::MemPlace {
    ptr,
    align,
    extra,
});

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

R
Ralf Jung 已提交
52 53 54 55 56 57
    /// 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 已提交
58 59
}

60
impl<'a> HashStable<StableHashingContext<'a>> for Place {
61 62 63 64
    fn hash_stable<W: StableHasherResult>(
        &self, hcx: &mut StableHashingContext<'a>,
        hasher: &mut StableHasher<W>) {

65 66 67 68 69 70 71 72 73 74
        match self {
            Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher),

            Place::Local { frame, local } => {
                frame.hash_stable(hcx, hasher);
                local.hash_stable(hcx, hasher);
            },
        }
    }
}
R
Ralf Jung 已提交
75 76 77 78 79 80 81 82
#[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;
83
    #[inline(always)]
R
Ralf Jung 已提交
84 85 86 87 88 89 90 91 92 93 94 95 96 97
    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;
98
    #[inline(always)]
R
Ralf Jung 已提交
99 100
    fn deref(&self) -> &MemPlace {
        &self.mplace
O
Oliver Schneider 已提交
101
    }
R
Ralf Jung 已提交
102 103 104
}

impl<'tcx> From<MPlaceTy<'tcx>> for PlaceTy<'tcx> {
105
    #[inline(always)]
R
Ralf Jung 已提交
106 107 108 109 110 111 112
    fn from(mplace: MPlaceTy<'tcx>) -> Self {
        PlaceTy {
            place: Place::Ptr(mplace.mplace),
            layout: mplace.layout
        }
    }
}
O
Oliver Schneider 已提交
113

R
Ralf Jung 已提交
114 115 116 117
impl MemPlace {
    #[inline(always)]
    pub fn from_scalar_ptr(ptr: Scalar, align: Align) -> Self {
        MemPlace {
118 119
            ptr,
            align,
120
            extra: None,
O
Oliver Schneider 已提交
121 122 123
        }
    }

R
Ralf Jung 已提交
124 125 126 127 128 129 130
    #[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) {
131
        assert_eq!(self.extra, None);
R
Ralf Jung 已提交
132 133 134 135 136 137
        (self.ptr, self.align)
    }

    /// Extract the ptr part of the mplace
    #[inline(always)]
    pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
B
Bernardo Meurer 已提交
138 139 140
        // 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 已提交
141 142 143 144 145
        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`.
146
    pub fn to_ref(self) -> Value {
R
Ralf Jung 已提交
147 148 149
        // We ignore the alignment of the place here -- special handling for packed structs ends
        // at the `&` operator.
        match self.extra {
150 151
            None => Value::Scalar(self.ptr.into()),
            Some(extra) => Value::ScalarPair(self.ptr.into(), extra.into()),
R
Ralf Jung 已提交
152 153 154 155 156 157 158 159 160 161 162
        }
    }
}

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]
163
    pub(super) fn len(self, cx: impl HasDataLayout) -> EvalResult<'tcx, u64> {
164 165 166 167 168 169
        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),
170
            }
171 172 173 174 175 176
        } 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),
177 178 179 180 181 182 183 184 185
            }
        }
    }

    #[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 已提交
186 187 188 189 190
        }
    }
}

impl<'tcx> OpTy<'tcx> {
191
    #[inline(always)]
R
Ralf Jung 已提交
192 193 194 195 196 197 198
    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),
        }
    }

199
    #[inline(always)]
R
Ralf Jung 已提交
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
    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]
218
    pub fn from_ptr(ptr: Pointer, align: Align) -> Self {
R
Ralf Jung 已提交
219
        Place::Ptr(MemPlace::from_ptr(ptr, align))
O
Oliver Schneider 已提交
220 221
    }

R
Ralf Jung 已提交
222 223
    #[inline]
    pub fn to_mem_place(self) -> MemPlace {
O
Oliver Schneider 已提交
224
        match self {
R
Ralf Jung 已提交
225 226
            Place::Ptr(mplace) => mplace,
            _ => bug!("to_mem_place: expected Place::Ptr, got {:?}", self),
O
Oliver Schneider 已提交
227 228 229 230

        }
    }

R
Ralf Jung 已提交
231 232 233
    #[inline]
    pub fn to_scalar_ptr_align(self) -> (Scalar, Align) {
        self.to_mem_place().to_scalar_ptr_align()
234
    }
235

R
Ralf Jung 已提交
236
    #[inline]
237
    pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
R
Ralf Jung 已提交
238 239 240
        self.to_mem_place().to_ptr()
    }
}
O
Oliver Schneider 已提交
241

R
Ralf Jung 已提交
242 243 244 245 246 247 248 249 250 251
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 已提交
252 253 254
    }
}

O
Oliver Schneider 已提交
255
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
R
Ralf Jung 已提交
256 257 258 259 260 261 262
    /// 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 已提交
263 264 265 266 267 268
        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 已提交
269 270 271 272 273 274 275 276 277
        };
        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 已提交
278
        &self,
R
Ralf Jung 已提交
279 280 281 282 283 284 285 286
        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, .. } => {
287 288 289
                let len = base.len(self)?;
                assert!(field < len, "Tried to access element {} of array/slice with length {}",
                    field, len);
R
Ralf Jung 已提交
290 291
                stride * field
            }
292
            layout::FieldPlacement::Union(count) => {
B
Bernardo Meurer 已提交
293 294
                assert!(field < count as u64,
                        "Tried to access field {} of union with {} fields", field, count);
295 296 297
                // Offset is always 0
                Size::from_bytes(0)
            }
R
Ralf Jung 已提交
298 299 300
        };
        // 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 已提交
301
        let field_layout = base.layout.field(self, usize::try_from(field).unwrap_or(0))?;
R
Ralf Jung 已提交
302

303 304 305 306 307
        // 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 已提交
308 309

        } else {
310 311 312
            // base.extra could be present; we might be accessing a sized field of an unsized
            // struct.
            (None, offset)
R
Ralf Jung 已提交
313 314
        };

315 316 317
        let ptr = base.ptr.ptr_offset(offset, self)?;
        let align = base.align.min(field_layout.align); // only use static information

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

321 322 323 324 325 326
    // 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> {
327
        let len = base.len(self)?; // also asserts that we have a type where this makes sense
328 329 330 331 332 333 334 335 336
        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 {
337
                mplace: MemPlace { ptr, align: base.align, extra: None },
338 339 340 341 342
                layout
            })
        }))
    }

R
Ralf Jung 已提交
343
    pub fn mplace_subslice(
O
Oliver Schneider 已提交
344
        &self,
R
Ralf Jung 已提交
345 346 347 348
        base: MPlaceTy<'tcx>,
        from: u64,
        to: u64,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
349
        let len = base.len(self)?; // also asserts that we have a type where this makes sense
R
Ralf Jung 已提交
350 351 352 353 354 355 356 357
        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 已提交
358
        };
R
Ralf Jung 已提交
359 360 361 362 363
        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 已提交
364 365
            // It is not nice to match on the type, but that seems to be the only way to
            // implement this.
V
varkor 已提交
366
            ty::Array(inner, _) =>
367 368
                (None, self.tcx.mk_array(inner, inner_len)),
            ty::Slice(..) => {
369
                let len = Scalar::from_uint(inner_len, self.pointer_size());
370 371
                (Some(len), base.layout.ty)
            }
R
Ralf Jung 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
            _ =>
                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
389
        assert_eq!(base.extra, None);
R
Ralf Jung 已提交
390
        Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base })
O
Oliver Schneider 已提交
391 392
    }

R
Ralf Jung 已提交
393 394
    /// Project into an mplace
    pub fn mplace_projection(
O
Oliver Schneider 已提交
395
        &self,
R
Ralf Jung 已提交
396 397 398
        base: MPlaceTy<'tcx>,
        proj_elem: &mir::PlaceElem<'tcx>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
399
        use rustc::mir::ProjectionElem::*;
R
Ralf Jung 已提交
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
        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,
            } => {
418
                let n = base.len(self)?;
R
Ralf Jung 已提交
419 420 421 422 423 424 425 426 427 428 429 430 431 432
                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 已提交
433 434
    }

R
Ralf Jung 已提交
435 436 437
    /// 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 已提交
438
        &mut self,
R
Ralf Jung 已提交
439
        base: PlaceTy<'tcx>,
R
Ralf Jung 已提交
440 441 442 443 444 445
        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 已提交
446 447
    }

R
Ralf Jung 已提交
448 449
    pub fn place_downcast(
        &mut self,
R
Ralf Jung 已提交
450
        base: PlaceTy<'tcx>,
R
Ralf Jung 已提交
451 452 453 454 455 456 457 458 459
        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 已提交
460
            }
R
Ralf Jung 已提交
461
        })
O
Oliver Schneider 已提交
462 463
    }

R
Ralf Jung 已提交
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
    /// 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()
            }
        })
    }

484 485 486 487 488 489
    /// 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>> {
490
        use rustc::mir::Place::*;
491
        Ok(match *mir_place {
492 493
            Promoted(ref promoted) => {
                let instance = self.frame().instance;
R
Ralf Jung 已提交
494
                let op = self.global_to_op(GlobalId {
495 496 497
                    instance,
                    promoted: Some(promoted.0),
                })?;
498
                let mplace = op.to_mem_place(); // these are always in memory
R
Ralf Jung 已提交
499
                let ty = self.monomorphize(promoted.1, self.substs());
500 501
                MPlaceTy {
                    mplace,
R
Ralf Jung 已提交
502
                    layout: self.layout_of(ty)?,
503 504 505
                }
            }

O
Oliver Schneider 已提交
506
            Static(ref static_) => {
R
Ralf Jung 已提交
507 508
                let ty = self.monomorphize(static_.ty, self.substs());
                let layout = self.layout_of(ty)?;
509 510 511 512 513
                let instance = ty::Instance::mono(*self.tcx, static_.def_id);
                let cid = GlobalId {
                    instance,
                    promoted: None
                };
R
Ralf Jung 已提交
514
                // Just create a lazy reference, so we can support recursive statics.
R
Ralf Jung 已提交
515
                // tcx takes are of assigning every static one and only one unique AllocId.
R
Ralf Jung 已提交
516 517
                // 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 已提交
518 519 520 521 522 523 524 525
                // 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());
526
                MPlaceTy::from_aligned_ptr(alloc.into(), layout)
O
Oliver Schneider 已提交
527 528
            }

529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
            _ => 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 已提交
550 551
            Projection(ref proj) => {
                let place = self.eval_place(&proj.base)?;
R
Ralf Jung 已提交
552
                self.place_projection(place, &proj.elem)?
O
Oliver Schneider 已提交
553
            }
554 555

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

R
Ralf Jung 已提交
558
        self.dump_place(place.place);
O
Oliver Schneider 已提交
559 560 561
        Ok(place)
    }

R
Ralf Jung 已提交
562 563
    /// Write a scalar to a place
    pub fn write_scalar(
O
Oliver Schneider 已提交
564
        &mut self,
R
Ralf Jung 已提交
565 566 567 568 569
        val: impl Into<ScalarMaybeUndef>,
        dest: PlaceTy<'tcx>,
    ) -> EvalResult<'tcx> {
        self.write_value(Value::Scalar(val.into()), dest)
    }
O
Oliver Schneider 已提交
570

R
Ralf Jung 已提交
571 572 573 574
    /// Write a value to a place
    pub fn write_value(
        &mut self,
        src_val: Value,
R
Ralf Jung 已提交
575
        dest: PlaceTy<'tcx>,
R
Ralf Jung 已提交
576
    ) -> EvalResult<'tcx> {
R
Ralf Jung 已提交
577
        trace!("write_value: {:?} <- {:?}", *dest, src_val);
R
Ralf Jung 已提交
578 579
        // 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 已提交
580
        let mplace = match dest.place {
O
Oliver Schneider 已提交
581
            Place::Local { frame, local } => {
R
Ralf Jung 已提交
582 583 584 585 586
                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(());
587
                    },
R
Ralf Jung 已提交
588
                    Operand::Indirect(mplace) => mplace, // already in memory
O
Oliver Schneider 已提交
589
                }
R
Ralf Jung 已提交
590
            },
R
Ralf Jung 已提交
591
            Place::Ptr(mplace) => mplace, // already in memory
O
Oliver Schneider 已提交
592 593
        };

R
Ralf Jung 已提交
594 595
        // This is already in memory, write there.
        let dest = MPlaceTy { mplace, layout: dest.layout };
R
Ralf Jung 已提交
596
        self.write_value_to_mplace(src_val, dest)
O
Oliver Schneider 已提交
597 598
    }

R
Ralf Jung 已提交
599 600 601 602 603 604
    /// Write a value to memory
    fn write_value_to_mplace(
        &mut self,
        value: Value,
        dest: MPlaceTy<'tcx>,
    ) -> EvalResult<'tcx> {
605
        let (ptr, ptr_align) = dest.to_scalar_ptr_align();
B
Bernardo Meurer 已提交
606 607 608 609
        // 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.
610 611 612 613 614 615 616 617

        // 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 已提交
618 619 620
        match value {
            Value::Scalar(scalar) => {
                self.memory.write_scalar(
621
                    ptr, ptr_align.min(dest.layout.align), scalar, dest.layout.size
R
Ralf Jung 已提交
622
                )
O
Oliver Schneider 已提交
623
            }
R
Ralf Jung 已提交
624 625 626
            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 已提交
627 628
                    _ => bug!("write_value_to_mplace: invalid ScalarPair layout: {:#?}",
                              dest.layout)
R
Ralf Jung 已提交
629 630 631 632
                };
                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);
633
                let b_ptr = ptr.offset(b_offset, &self)?.into();
634

635 636
                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 已提交
637
            }
R
Ralf Jung 已提交
638
        }
O
Oliver Schneider 已提交
639 640
    }

R
Ralf Jung 已提交
641 642
    /// Copy the data from an operand to a place
    pub fn copy_op(
O
Oliver Schneider 已提交
643
        &mut self,
R
Ralf Jung 已提交
644 645 646
        src: OpTy<'tcx>,
        dest: PlaceTy<'tcx>,
    ) -> EvalResult<'tcx> {
647 648
        assert!(!src.layout.is_unsized() && !dest.layout.is_unsized(),
            "Cannot copy unsized data");
R
Ralf Jung 已提交
649 650
        assert_eq!(src.layout.size, dest.layout.size,
            "Size mismatch when copying!\nsrc: {:#?}\ndest: {:#?}", src, dest);
R
Ralf Jung 已提交
651 652 653 654

        // 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 已提交
655 656 657 658
                // 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 已提交
659 660 661
            Err(mplace) => mplace.to_scalar_ptr_align(),
        };
        // Slow path, this does not fit into an immediate. Just memcpy.
R
Ralf Jung 已提交
662
        trace!("copy_op: {:?} <- {:?}", *dest, *src);
R
Ralf Jung 已提交
663 664 665 666 667 668
        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 已提交
669 670
    }

R
Ralf Jung 已提交
671 672 673
    /// 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 已提交
674
        &mut self,
R
Ralf Jung 已提交
675 676 677 678
        place: PlaceTy<'tcx>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
        let mplace = match place.place {
            Place::Local { frame, local } => {
679 680 681 682 683 684 685 686
                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 已提交
687 688
                        // that might e.g. be an inner field of a struct with `Scalar` layout,
                        // that has different alignment than the outer field.
689 690 691 692 693 694 695 696 697 698
                        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 已提交
699 700 701 702 703
            }
            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 已提交
704 705
    }

R
Ralf Jung 已提交
706
    pub fn allocate(
O
Oliver Schneider 已提交
707
        &mut self,
R
Ralf Jung 已提交
708 709 710 711 712 713 714
        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 已提交
715

716
    pub fn write_discriminant_index(
R
Ralf Jung 已提交
717 718 719 720 721 722
        &mut self,
        variant_index: usize,
        dest: PlaceTy<'tcx>,
    ) -> EvalResult<'tcx> {
        match dest.layout.variants {
            layout::Variants::Single { index } => {
R
Ralf Jung 已提交
723
                assert_eq!(index, variant_index);
O
Oliver Schneider 已提交
724
            }
R
Ralf Jung 已提交
725
            layout::Variants::Tagged { ref tag, .. } => {
726
                let adt_def = dest.layout.ty.ty_adt_def().unwrap();
R
Ralf Jung 已提交
727
                assert!(variant_index < adt_def.variants.len());
728
                let discr_val = adt_def
R
Ralf Jung 已提交
729 730 731 732 733 734 735 736 737 738 739
                    .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)?;
740
                self.write_scalar(Scalar::from_uint(discr_val, size), discr_dest)?;
O
Oliver Schneider 已提交
741
            }
R
Ralf Jung 已提交
742 743 744 745 746
            layout::Variants::NicheFilling {
                dataful_variant,
                ref niche_variants,
                niche_start,
                ..
O
Oliver Schneider 已提交
747
            } => {
R
Ralf Jung 已提交
748
                assert!(variant_index < dest.layout.ty.ty_adt_def().unwrap().variants.len());
R
Ralf Jung 已提交
749 750 751 752 753
                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);
754 755 756 757
                    self.write_scalar(
                        Scalar::from_uint(niche_value, niche_dest.layout.size),
                        niche_dest
                    )?;
R
Ralf Jung 已提交
758
                }
O
Oliver Schneider 已提交
759
            }
760
        }
R
Ralf Jung 已提交
761 762

        Ok(())
O
Oliver Schneider 已提交
763 764
    }

R
Ralf Jung 已提交
765
    /// Every place can be read from, so we can turm them into an operand
766
    #[inline(always)]
R
Ralf Jung 已提交
767 768 769 770 771 772 773 774 775
    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 已提交
776
    }
777

778 779 780 781 782 783 784 785 786
    /// 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
787 788 789 790 791
        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
        }
792 793 794

        let mplace = MPlaceTy {
            mplace: MemPlace { extra: None, ..*mplace },
795
            layout
796 797
        };
        Ok((instance, mplace))
798
    }
O
Oliver Schneider 已提交
799
}