rustc_smir/rustc_smir/
alloc.rs

1use rustc_abi::{Align, Size};
2use rustc_middle::mir::ConstValue;
3use rustc_middle::mir::interpret::{AllocInit, AllocRange, Pointer, alloc_range};
4use stable_mir::Error;
5use stable_mir::mir::Mutability;
6use stable_mir::ty::{Allocation, ProvenanceMap};
7
8use crate::rustc_smir::{Stable, Tables};
9use crate::stable_mir;
10
11/// Creates new empty `Allocation` from given `Align`.
12fn new_empty_allocation(align: Align) -> Allocation {
13    Allocation {
14        bytes: Vec::new(),
15        provenance: ProvenanceMap { ptrs: Vec::new() },
16        align: align.bytes(),
17        mutability: Mutability::Not,
18    }
19}
20
21// We need this method instead of a Stable implementation
22// because we need to get `Ty` of the const we are trying to create, to do that
23// we need to have access to `ConstantKind` but we can't access that inside Stable impl.
24#[allow(rustc::usage_of_qualified_ty)]
25pub(crate) fn new_allocation<'tcx>(
26    ty: rustc_middle::ty::Ty<'tcx>,
27    const_value: ConstValue<'tcx>,
28    tables: &mut Tables<'tcx>,
29) -> Allocation {
30    try_new_allocation(ty, const_value, tables)
31        .unwrap_or_else(|_| panic!("Failed to convert: {const_value:?} to {ty:?}"))
32}
33
34#[allow(rustc::usage_of_qualified_ty)]
35pub(crate) fn try_new_allocation<'tcx>(
36    ty: rustc_middle::ty::Ty<'tcx>,
37    const_value: ConstValue<'tcx>,
38    tables: &mut Tables<'tcx>,
39) -> Result<Allocation, Error> {
40    let layout = tables
41        .tcx
42        .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty))
43        .map_err(|e| e.stable(tables))?;
44    Ok(match const_value {
45        ConstValue::Scalar(scalar) => {
46            let size = scalar.size();
47            let mut allocation = rustc_middle::mir::interpret::Allocation::new(
48                size,
49                layout.align.abi,
50                AllocInit::Uninit,
51                (),
52            );
53            allocation
54                .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar)
55                .map_err(|e| e.stable(tables))?;
56            allocation.stable(tables)
57        }
58        ConstValue::ZeroSized => new_empty_allocation(layout.align.abi),
59        ConstValue::Slice { data, meta } => {
60            let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data);
61            let ptr = Pointer::new(alloc_id.into(), Size::ZERO);
62            let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx);
63            let scalar_meta =
64                rustc_middle::mir::interpret::Scalar::from_target_usize(meta, &tables.tcx);
65            let mut allocation = rustc_middle::mir::interpret::Allocation::new(
66                layout.size,
67                layout.align.abi,
68                AllocInit::Uninit,
69                (),
70            );
71            allocation
72                .write_scalar(
73                    &tables.tcx,
74                    alloc_range(Size::ZERO, tables.tcx.data_layout.pointer_size),
75                    scalar_ptr,
76                )
77                .map_err(|e| e.stable(tables))?;
78            allocation
79                .write_scalar(
80                    &tables.tcx,
81                    alloc_range(tables.tcx.data_layout.pointer_size, scalar_meta.size()),
82                    scalar_meta,
83                )
84                .map_err(|e| e.stable(tables))?;
85            allocation.stable(tables)
86        }
87        ConstValue::Indirect { alloc_id, offset } => {
88            let alloc = tables.tcx.global_alloc(alloc_id).unwrap_memory();
89            allocation_filter(&alloc.0, alloc_range(offset, layout.size), tables)
90        }
91    })
92}
93
94/// Creates an `Allocation` only from information within the `AllocRange`.
95pub(super) fn allocation_filter<'tcx>(
96    alloc: &rustc_middle::mir::interpret::Allocation,
97    alloc_range: AllocRange,
98    tables: &mut Tables<'tcx>,
99) -> Allocation {
100    let mut bytes: Vec<Option<u8>> = alloc
101        .inspect_with_uninit_and_ptr_outside_interpreter(
102            alloc_range.start.bytes_usize()..alloc_range.end().bytes_usize(),
103        )
104        .iter()
105        .copied()
106        .map(Some)
107        .collect();
108    for (i, b) in bytes.iter_mut().enumerate() {
109        if !alloc.init_mask().get(Size::from_bytes(i + alloc_range.start.bytes_usize())) {
110            *b = None;
111        }
112    }
113    let mut ptrs = Vec::new();
114    for (offset, prov) in alloc
115        .provenance()
116        .ptrs()
117        .iter()
118        .filter(|a| a.0 >= alloc_range.start && a.0 <= alloc_range.end())
119    {
120        ptrs.push((
121            offset.bytes_usize() - alloc_range.start.bytes_usize(),
122            tables.prov(prov.alloc_id()),
123        ));
124    }
125    Allocation {
126        bytes,
127        provenance: ProvenanceMap { ptrs },
128        align: alloc.align.bytes(),
129        mutability: alloc.mutability.stable(tables),
130    }
131}