place.rs 32.2 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
//! 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;
16
use std::hash::Hash;
R
Ralf Jung 已提交
17

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

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

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

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

R
Ralf Jung 已提交
48 49 50 51 52 53
    /// 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 已提交
54 55
}

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

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

/// A MemPlace with its layout. Constructing it is only possible in this module.
#[derive(Copy, Clone, Debug)]
72 73
pub struct MPlaceTy<'tcx, Tag=()> {
    mplace: MemPlace<Tag>,
R
Ralf Jung 已提交
74 75 76
    pub layout: TyLayout<'tcx>,
}

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

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

R
Ralf Jung 已提交
95
impl MemPlace {
96 97 98 99 100 101 102
    #[inline]
    pub fn with_default_tag<Tag>(self) -> MemPlace<Tag>
        where Tag: Default
    {
        MemPlace {
            ptr: self.ptr.with_default_tag(),
            align: self.align,
R
Ralf Jung 已提交
103
            meta: self.meta.map(Scalar::with_default_tag),
104 105 106 107 108 109 110 111 112 113 114
        }
    }
}

impl<Tag> MemPlace<Tag> {
    #[inline]
    pub fn erase_tag(self) -> MemPlace
    {
        MemPlace {
            ptr: self.ptr.erase_tag(),
            align: self.align,
R
Ralf Jung 已提交
115
            meta: self.meta.map(Scalar::erase_tag),
116 117 118
        }
    }

R
Ralf Jung 已提交
119
    #[inline(always)]
120
    pub fn from_scalar_ptr(ptr: Scalar<Tag>, align: Align) -> Self {
R
Ralf Jung 已提交
121
        MemPlace {
122 123
            ptr,
            align,
R
Ralf Jung 已提交
124
            meta: None,
O
Oliver Schneider 已提交
125 126 127
        }
    }

R
Ralf Jung 已提交
128
    #[inline(always)]
129
    pub fn from_ptr(ptr: Pointer<Tag>, align: Align) -> Self {
R
Ralf Jung 已提交
130 131 132 133
        Self::from_scalar_ptr(ptr.into(), align)
    }

    #[inline(always)]
134
    pub fn to_scalar_ptr_align(self) -> (Scalar<Tag>, Align) {
R
Ralf Jung 已提交
135
        assert!(self.meta.is_none());
R
Ralf Jung 已提交
136 137 138
        (self.ptr, self.align)
    }

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

160
impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
R
Ralf Jung 已提交
161 162 163 164 165 166 167 168 169 170 171 172
    /// Produces a MemPlace that works for ZST but nothing else
    #[inline]
    pub fn dangling(layout: TyLayout<'tcx>, cx: impl HasDataLayout) -> Self {
        MPlaceTy {
            mplace: MemPlace::from_scalar_ptr(
                Scalar::from_uint(layout.align.abi(), cx.pointer_size()),
                layout.align
            ),
            layout
        }
    }

R
Ralf Jung 已提交
173
    #[inline]
174
    fn from_aligned_ptr(ptr: Pointer<Tag>, layout: TyLayout<'tcx>) -> Self {
R
Ralf Jung 已提交
175 176 177 178
        MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align), layout }
    }

    #[inline]
179
    pub(super) fn len(self, cx: impl HasDataLayout) -> EvalResult<'tcx, u64> {
180
        if self.layout.is_unsized() {
R
Ralf Jung 已提交
181
            // We need to consult `meta` metadata
182 183
            match self.layout.ty.sty {
                ty::Slice(..) | ty::Str =>
R
Ralf Jung 已提交
184
                    return self.mplace.meta.unwrap().to_usize(cx),
185
                _ => bug!("len not supported on unsized type {:?}", self.layout.ty),
186
            }
187 188 189 190 191 192
        } 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),
193 194 195 196 197
            }
        }
    }

    #[inline]
198
    pub(super) fn vtable(self) -> EvalResult<'tcx, Pointer<Tag>> {
199
        match self.layout.ty.sty {
R
Ralf Jung 已提交
200
            ty::Dynamic(..) => self.mplace.meta.unwrap().to_ptr(),
201
            _ => bug!("vtable not supported on type {:?}", self.layout.ty),
R
Ralf Jung 已提交
202 203 204 205
        }
    }
}

206
impl<'tcx, Tag: ::std::fmt::Debug> OpTy<'tcx, Tag> {
207
    #[inline(always)]
208 209
    pub fn try_as_mplace(self) -> Result<MPlaceTy<'tcx, Tag>, Value<Tag>> {
        match self.op {
R
Ralf Jung 已提交
210 211 212 213 214
            Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }),
            Operand::Immediate(value) => Err(value),
        }
    }

215
    #[inline(always)]
216
    pub fn to_mem_place(self) -> MPlaceTy<'tcx, Tag> {
R
Ralf Jung 已提交
217 218 219 220
        self.try_as_mplace().unwrap()
    }
}

221
impl<'tcx, Tag: ::std::fmt::Debug> Place<Tag> {
R
Ralf Jung 已提交
222 223 224 225 226 227 228
    /// 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]
229
    pub fn from_scalar_ptr(ptr: Scalar<Tag>, align: Align) -> Self {
R
Ralf Jung 已提交
230 231 232 233
        Place::Ptr(MemPlace::from_scalar_ptr(ptr, align))
    }

    #[inline]
234
    pub fn from_ptr(ptr: Pointer<Tag>, align: Align) -> Self {
R
Ralf Jung 已提交
235
        Place::Ptr(MemPlace::from_ptr(ptr, align))
O
Oliver Schneider 已提交
236 237
    }

R
Ralf Jung 已提交
238
    #[inline]
239
    pub fn to_mem_place(self) -> MemPlace<Tag> {
O
Oliver Schneider 已提交
240
        match self {
R
Ralf Jung 已提交
241 242
            Place::Ptr(mplace) => mplace,
            _ => bug!("to_mem_place: expected Place::Ptr, got {:?}", self),
O
Oliver Schneider 已提交
243 244 245 246

        }
    }

R
Ralf Jung 已提交
247
    #[inline]
248
    pub fn to_scalar_ptr_align(self) -> (Scalar<Tag>, Align) {
R
Ralf Jung 已提交
249
        self.to_mem_place().to_scalar_ptr_align()
250
    }
251

R
Ralf Jung 已提交
252
    #[inline]
253
    pub fn to_ptr(self) -> EvalResult<'tcx, Pointer<Tag>> {
R
Ralf Jung 已提交
254 255 256
        self.to_mem_place().to_ptr()
    }
}
O
Oliver Schneider 已提交
257

258
impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> {
R
Ralf Jung 已提交
259 260 261 262 263 264 265
    /// 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]
266
    pub fn to_mem_place(self) -> MPlaceTy<'tcx, Tag> {
R
Ralf Jung 已提交
267
        MPlaceTy { mplace: self.place.to_mem_place(), layout: self.layout }
O
Oliver Schneider 已提交
268 269 270
    }
}

271
// separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385
272 273 274 275 276
impl<'a, 'mir, 'tcx, Tag, M> EvalContext<'a, 'mir, 'tcx, M>
where
    Tag: ::std::fmt::Debug+Default+Copy+Eq+Hash+'static,
    M: Machine<'a, 'mir, 'tcx, PointerTag=Tag>,
    M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag>)>,
277
{
R
Ralf Jung 已提交
278 279 280
    /// 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(
281 282
        &self, val: ValTy<'tcx, M::PointerTag>
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
R
Ralf Jung 已提交
283 284
        let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty;
        let layout = self.layout_of(pointee_type)?;
R
Ralf Jung 已提交
285 286 287
        let align = layout.align;
        let mplace = match *val {
            Value::Scalar(ptr) =>
R
Ralf Jung 已提交
288 289 290
                MemPlace { ptr: ptr.not_undef()?, align, meta: None },
            Value::ScalarPair(ptr, meta) =>
                MemPlace { ptr: ptr.not_undef()?, align, meta: Some(meta.not_undef()?) },
R
Ralf Jung 已提交
291 292 293 294 295 296 297 298 299
        };
        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 已提交
300
        &self,
301
        base: MPlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
302
        field: u64,
303
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
R
Ralf Jung 已提交
304 305 306 307 308
        // 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, .. } => {
309 310 311
                let len = base.len(self)?;
                assert!(field < len, "Tried to access element {} of array/slice with length {}",
                    field, len);
R
Ralf Jung 已提交
312 313
                stride * field
            }
314
            layout::FieldPlacement::Union(count) => {
B
Bernardo Meurer 已提交
315 316
                assert!(field < count as u64,
                        "Tried to access field {} of union with {} fields", field, count);
317 318 319
                // Offset is always 0
                Size::from_bytes(0)
            }
R
Ralf Jung 已提交
320 321 322
        };
        // 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 已提交
323
        let field_layout = base.layout.field(self, usize::try_from(field).unwrap_or(0))?;
R
Ralf Jung 已提交
324

325
        // Offset may need adjustment for unsized fields
R
Ralf Jung 已提交
326
        let (meta, offset) = if field_layout.is_unsized() {
327
            // re-use parent metadata to determine dynamic field layout
R
Ralf Jung 已提交
328 329
            let (_, align) = self.size_and_align_of(base.meta, field_layout)?;
            (base.meta, offset.abi_align(align))
R
Ralf Jung 已提交
330 331

        } else {
R
Ralf Jung 已提交
332
            // base.meta could be present; we might be accessing a sized field of an unsized
333 334
            // struct.
            (None, offset)
R
Ralf Jung 已提交
335 336
        };

337
        let ptr = base.ptr.ptr_offset(offset, self)?;
338 339 340 341
        let align = base.align
            // We do not look at `base.layout.align` nor `field_layout.align`, unlike
            // codegen -- mostly to see if we can get away with that
            .restrict_for_offset(offset); // must be last thing that happens
342

R
Ralf Jung 已提交
343
        Ok(MPlaceTy { mplace: MemPlace { ptr, align, meta }, layout: field_layout })
O
Oliver Schneider 已提交
344 345
    }

346 347 348 349
    // 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,
350 351 352 353
        base: MPlaceTy<'tcx, Tag>,
    ) ->
        EvalResult<'tcx, impl Iterator<Item=EvalResult<'tcx, MPlaceTy<'tcx, Tag>>> + 'a>
    {
354
        let len = base.len(self)?; // also asserts that we have a type where this makes sense
355 356 357 358 359 360 361 362 363
        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 {
R
Ralf Jung 已提交
364
                mplace: MemPlace { ptr, align: base.align, meta: None },
365 366 367 368 369
                layout
            })
        }))
    }

R
Ralf Jung 已提交
370
    pub fn mplace_subslice(
O
Oliver Schneider 已提交
371
        &self,
372
        base: MPlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
373 374
        from: u64,
        to: u64,
375
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
376
        let len = base.len(self)?; // also asserts that we have a type where this makes sense
R
Ralf Jung 已提交
377 378 379 380 381 382 383 384
        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 已提交
385
        };
R
Ralf Jung 已提交
386 387
        let ptr = base.ptr.ptr_offset(from_offset, self)?;

R
Ralf Jung 已提交
388
        // Compute meta and new layout
R
Ralf Jung 已提交
389
        let inner_len = len - to - from;
R
Ralf Jung 已提交
390
        let (meta, ty) = match base.layout.ty.sty {
R
Ralf Jung 已提交
391 392
            // It is not nice to match on the type, but that seems to be the only way to
            // implement this.
V
varkor 已提交
393
            ty::Array(inner, _) =>
394 395
                (None, self.tcx.mk_array(inner, inner_len)),
            ty::Slice(..) => {
396
                let len = Scalar::from_uint(inner_len, self.pointer_size());
397 398
                (Some(len), base.layout.ty)
            }
R
Ralf Jung 已提交
399 400 401 402 403 404
            _ =>
                bug!("cannot subslice non-array type: `{:?}`", base.layout.ty),
        };
        let layout = self.layout_of(ty)?;

        Ok(MPlaceTy {
R
Ralf Jung 已提交
405
            mplace: MemPlace { ptr, align: base.align, meta },
R
Ralf Jung 已提交
406 407 408 409 410 411
            layout
        })
    }

    pub fn mplace_downcast(
        &self,
412
        base: MPlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
413
        variant: usize,
414
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
R
Ralf Jung 已提交
415
        // Downcasts only change the layout
R
Ralf Jung 已提交
416
        assert!(base.meta.is_none());
R
Ralf Jung 已提交
417
        Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base })
O
Oliver Schneider 已提交
418 419
    }

R
Ralf Jung 已提交
420 421
    /// Project into an mplace
    pub fn mplace_projection(
O
Oliver Schneider 已提交
422
        &self,
423
        base: MPlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
424
        proj_elem: &mir::PlaceElem<'tcx>,
425
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
426
        use rustc::mir::ProjectionElem::*;
R
Ralf Jung 已提交
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
        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,
            } => {
445
                let n = base.len(self)?;
R
Ralf Jung 已提交
446 447 448 449 450 451 452 453 454 455 456 457 458 459
                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 已提交
460 461
    }

R
Ralf Jung 已提交
462 463 464
    /// 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 已提交
465
        &mut self,
466
        base: PlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
467
        field: u64,
468
    ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
R
Ralf Jung 已提交
469 470 471 472
        // 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 已提交
473 474
    }

R
Ralf Jung 已提交
475 476
    pub fn place_downcast(
        &mut self,
477
        base: PlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
478
        variant: usize,
479
    ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
R
Ralf Jung 已提交
480 481 482 483 484 485 486
        // 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 已提交
487
            }
R
Ralf Jung 已提交
488
        })
O
Oliver Schneider 已提交
489 490
    }

R
Ralf Jung 已提交
491 492 493
    /// Project into a place
    pub fn place_projection(
        &mut self,
494
        base: PlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
495
        proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>,
496
    ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
R
Ralf Jung 已提交
497 498 499 500 501 502 503 504 505 506 507 508 509 510
        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()
            }
        })
    }

511 512 513 514 515
    /// 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>
516
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
517
        use rustc::mir::Place::*;
518
        Ok(match *mir_place {
519 520
            Promoted(ref promoted) => {
                let instance = self.frame().instance;
R
Ralf Jung 已提交
521
                let op = self.global_to_op(GlobalId {
522 523 524
                    instance,
                    promoted: Some(promoted.0),
                })?;
525
                let mplace = op.to_mem_place(); // these are always in memory
R
Ralf Jung 已提交
526
                let ty = self.monomorphize(promoted.1, self.substs());
527 528
                MPlaceTy {
                    mplace,
R
Ralf Jung 已提交
529
                    layout: self.layout_of(ty)?,
530 531 532
                }
            }

O
Oliver Schneider 已提交
533
            Static(ref static_) => {
R
Ralf Jung 已提交
534 535
                let ty = self.monomorphize(static_.ty, self.substs());
                let layout = self.layout_of(ty)?;
536 537 538 539 540
                let instance = ty::Instance::mono(*self.tcx, static_.def_id);
                let cid = GlobalId {
                    instance,
                    promoted: None
                };
R
Ralf Jung 已提交
541
                // Just create a lazy reference, so we can support recursive statics.
R
Ralf Jung 已提交
542
                // tcx takes are of assigning every static one and only one unique AllocId.
R
Ralf Jung 已提交
543 544
                // 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 已提交
545 546 547 548 549 550 551 552
                // 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());
553
                MPlaceTy::from_aligned_ptr(Pointer::from(alloc).with_default_tag(), layout)
O
Oliver Schneider 已提交
554 555
            }

556 557 558 559 560 561
            _ => 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`.
R
Ralf Jung 已提交
562 563 564 565
    pub fn eval_place(
        &mut self,
        mir_place: &mir::Place<'tcx>
    ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
566 567 568 569 570 571 572 573 574 575 576 577 578 579
        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 已提交
580 581
            Projection(ref proj) => {
                let place = self.eval_place(&proj.base)?;
R
Ralf Jung 已提交
582
                self.place_projection(place, &proj.elem)?
O
Oliver Schneider 已提交
583
            }
584 585

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

R
Ralf Jung 已提交
588
        self.dump_place(place.place);
O
Oliver Schneider 已提交
589 590 591
        Ok(place)
    }

R
Ralf Jung 已提交
592 593
    /// Write a scalar to a place
    pub fn write_scalar(
O
Oliver Schneider 已提交
594
        &mut self,
595 596
        val: impl Into<ScalarMaybeUndef<M::PointerTag>>,
        dest: PlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
597 598 599
    ) -> EvalResult<'tcx> {
        self.write_value(Value::Scalar(val.into()), dest)
    }
O
Oliver Schneider 已提交
600

R
Ralf Jung 已提交
601 602 603
    /// Write a value to a place
    pub fn write_value(
        &mut self,
604 605
        src_val: Value<M::PointerTag>,
        dest: PlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
606
    ) -> EvalResult<'tcx> {
R
Ralf Jung 已提交
607
        trace!("write_value: {:?} <- {:?}", *dest, src_val);
608 609 610 611 612 613 614
        // Check that the value actually is okay for that type
        if M::ENFORCE_VALIDITY {
            // Something changed somewhere, better make sure it matches the type!
            let op = OpTy { op: Operand::Immediate(src_val), layout: dest.layout };
            self.validate_operand(op, &mut vec![], None, /*const_mode*/false)?;
        }

R
Ralf Jung 已提交
615 616
        // 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 已提交
617
        let mplace = match dest.place {
O
Oliver Schneider 已提交
618
            Place::Local { frame, local } => {
R
Ralf Jung 已提交
619 620 621 622 623
                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(());
624
                    },
R
Ralf Jung 已提交
625
                    Operand::Indirect(mplace) => mplace, // already in memory
O
Oliver Schneider 已提交
626
                }
R
Ralf Jung 已提交
627
            },
R
Ralf Jung 已提交
628
            Place::Ptr(mplace) => mplace, // already in memory
O
Oliver Schneider 已提交
629 630
        };

R
Ralf Jung 已提交
631 632
        // This is already in memory, write there.
        let dest = MPlaceTy { mplace, layout: dest.layout };
R
Ralf Jung 已提交
633
        self.write_value_to_mplace(src_val, dest)
O
Oliver Schneider 已提交
634 635
    }

636 637
    /// Write a value to memory. This does NOT do validation, so you better had already
    /// done that before calling this!
R
Ralf Jung 已提交
638 639
    fn write_value_to_mplace(
        &mut self,
640 641
        value: Value<M::PointerTag>,
        dest: MPlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
642
    ) -> EvalResult<'tcx> {
643
        let (ptr, ptr_align) = dest.to_scalar_ptr_align();
B
Bernardo Meurer 已提交
644 645 646 647
        // 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.
648 649

        // Nothing to do for ZSTs, other than checking alignment
650
        if dest.layout.is_zst() {
651 652 653 654 655
            self.memory.check_align(ptr, ptr_align)?;
            return Ok(());
        }

        let ptr = ptr.to_ptr()?;
R
Ralf Jung 已提交
656 657 658
        match value {
            Value::Scalar(scalar) => {
                self.memory.write_scalar(
659
                    ptr, ptr_align.min(dest.layout.align), scalar, dest.layout.size
R
Ralf Jung 已提交
660
                )
O
Oliver Schneider 已提交
661
            }
R
Ralf Jung 已提交
662 663 664
            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 已提交
665 666
                    _ => bug!("write_value_to_mplace: invalid ScalarPair layout: {:#?}",
                              dest.layout)
R
Ralf Jung 已提交
667 668 669 670
                };
                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);
671
                let b_ptr = ptr.offset(b_offset, &self)?.into();
672

673 674
                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 已提交
675
            }
R
Ralf Jung 已提交
676
        }
O
Oliver Schneider 已提交
677 678
    }

R
Ralf Jung 已提交
679 680
    /// Copy the data from an operand to a place
    pub fn copy_op(
O
Oliver Schneider 已提交
681
        &mut self,
682 683
        src: OpTy<'tcx, M::PointerTag>,
        dest: PlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
684
    ) -> EvalResult<'tcx> {
685 686
        assert!(!src.layout.is_unsized() && !dest.layout.is_unsized(),
            "Cannot copy unsized data");
R
Ralf Jung 已提交
687 688
        assert_eq!(src.layout.size, dest.layout.size,
            "Size mismatch when copying!\nsrc: {:#?}\ndest: {:#?}", src, dest);
R
Ralf Jung 已提交
689 690 691 692

        // 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 已提交
693 694 695 696
                // 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 已提交
697 698 699
            Err(mplace) => mplace.to_scalar_ptr_align(),
        };
        // Slow path, this does not fit into an immediate. Just memcpy.
R
Ralf Jung 已提交
700
        trace!("copy_op: {:?} <- {:?}", *dest, *src);
701 702
        let dest = self.force_allocation(dest)?;
        let (dest_ptr, dest_align) = dest.to_scalar_ptr_align();
R
Ralf Jung 已提交
703 704 705 706
        self.memory.copy(
            src_ptr, src_align,
            dest_ptr, dest_align,
            src.layout.size, false
707 708 709 710 711 712
        )?;
        if M::ENFORCE_VALIDITY {
            // Something changed somewhere, better make sure it matches the type!
            self.validate_operand(dest.into(), &mut vec![], None, /*const_mode*/false)?;
        }
        Ok(())
O
Oliver Schneider 已提交
713 714
    }

R
Ralf Jung 已提交
715 716 717
    /// 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 已提交
718
        &mut self,
719 720
        place: PlaceTy<'tcx, M::PointerTag>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
R
Ralf Jung 已提交
721 722
        let mplace = match place.place {
            Place::Local { frame, local } => {
723 724 725 726 727 728 729 730
                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 已提交
731 732
                        // that might e.g. be an inner field of a struct with `Scalar` layout,
                        // that has different alignment than the outer field.
733 734
                        let local_layout = self.layout_of_local(frame, local)?;
                        let ptr = self.allocate(local_layout, MemoryKind::Stack)?;
735 736
                        // We don't have to validate as we can assume the local
                        // was already valid for its type.
737 738 739 740 741 742 743 744
                        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 已提交
745 746 747 748 749
            }
            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 已提交
750 751
    }

R
Ralf Jung 已提交
752
    pub fn allocate(
O
Oliver Schneider 已提交
753
        &mut self,
R
Ralf Jung 已提交
754 755
        layout: TyLayout<'tcx>,
        kind: MemoryKind<M::MemoryKinds>,
756
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
R
Ralf Jung 已提交
757 758 759 760
        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 已提交
761

762
    pub fn write_discriminant_index(
R
Ralf Jung 已提交
763 764
        &mut self,
        variant_index: usize,
765
        dest: PlaceTy<'tcx, M::PointerTag>,
R
Ralf Jung 已提交
766 767 768
    ) -> EvalResult<'tcx> {
        match dest.layout.variants {
            layout::Variants::Single { index } => {
R
Ralf Jung 已提交
769
                assert_eq!(index, variant_index);
O
Oliver Schneider 已提交
770
            }
R
Ralf Jung 已提交
771
            layout::Variants::Tagged { ref tag, .. } => {
772
                let adt_def = dest.layout.ty.ty_adt_def().unwrap();
R
Ralf Jung 已提交
773
                assert!(variant_index < adt_def.variants.len());
774
                let discr_val = adt_def
R
Ralf Jung 已提交
775 776 777 778 779 780 781 782 783 784 785
                    .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)?;
786
                self.write_scalar(Scalar::from_uint(discr_val, size), discr_dest)?;
O
Oliver Schneider 已提交
787
            }
R
Ralf Jung 已提交
788 789 790 791 792
            layout::Variants::NicheFilling {
                dataful_variant,
                ref niche_variants,
                niche_start,
                ..
O
Oliver Schneider 已提交
793
            } => {
R
Ralf Jung 已提交
794
                assert!(variant_index < dest.layout.ty.ty_adt_def().unwrap().variants.len());
R
Ralf Jung 已提交
795 796 797 798 799
                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);
800 801 802 803
                    self.write_scalar(
                        Scalar::from_uint(niche_value, niche_dest.layout.size),
                        niche_dest
                    )?;
R
Ralf Jung 已提交
804
                }
O
Oliver Schneider 已提交
805
            }
806
        }
R
Ralf Jung 已提交
807 808

        Ok(())
O
Oliver Schneider 已提交
809 810
    }

R
Ralf Jung 已提交
811
    /// Every place can be read from, so we can turm them into an operand
812
    #[inline(always)]
R
Ralf Jung 已提交
813 814 815 816
    pub fn place_to_op(
        &self,
        place: PlaceTy<'tcx, M::PointerTag>
    ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> {
R
Ralf Jung 已提交
817 818 819 820 821 822 823 824
        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 已提交
825
    }
826

827 828
    /// 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.
829 830
    pub(super) fn unpack_dyn_trait(&self, mplace: MPlaceTy<'tcx, M::PointerTag>)
    -> EvalResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> {
831 832 833 834 835
        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
836 837 838 839 840
        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
        }
841 842

        let mplace = MPlaceTy {
R
Ralf Jung 已提交
843
            mplace: MemPlace { meta: None, ..*mplace },
844
            layout
845 846
        };
        Ok((instance, mplace))
847
    }
O
Oliver Schneider 已提交
848
}