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