| //! Internal memory allocator implementation for rustc_public. |
| //! |
| //! This module handles all direct interactions with rustc queries and performs |
| //! the actual memory allocations. The stable interface in `rustc_public::alloc` |
| //! delegates all query-related operations to this implementation. |
| |
| use rustc_abi::{Size, TyAndLayout}; |
| use rustc_middle::mir::interpret::{ |
| AllocId, AllocInit, AllocRange, Allocation, ConstAllocation, Pointer, Scalar, alloc_range, |
| }; |
| use rustc_middle::ty::{Ty, layout}; |
| |
| use super::{CompilerCtxt, Tables}; |
| use crate::bridge::Allocation as _; |
| use crate::{Bridge, Error}; |
| |
| pub fn create_ty_and_layout<'tcx, B: Bridge>( |
| cx: &CompilerCtxt<'tcx, B>, |
| ty: Ty<'tcx>, |
| ) -> Result<TyAndLayout<'tcx, Ty<'tcx>>, &'tcx layout::LayoutError<'tcx>> { |
| use crate::context::TypingEnvHelpers; |
| cx.tcx.layout_of(cx.fully_monomorphized().as_query_input(ty)) |
| } |
| |
| pub fn try_new_scalar<'tcx, B: Bridge>( |
| layout: TyAndLayout<'tcx, Ty<'tcx>>, |
| scalar: Scalar, |
| cx: &CompilerCtxt<'tcx, B>, |
| ) -> Result<Allocation, B::Error> { |
| let size = scalar.size(); |
| let mut allocation = Allocation::new(size, layout.align.abi, AllocInit::Uninit, ()); |
| allocation |
| .write_scalar(&cx.tcx, alloc_range(Size::ZERO, size), scalar) |
| .map_err(|e| B::Error::from_internal(e))?; |
| |
| Ok(allocation) |
| } |
| |
| pub fn try_new_slice<'tcx, B: Bridge>( |
| layout: TyAndLayout<'tcx, Ty<'tcx>>, |
| alloc_id: AllocId, |
| meta: u64, |
| cx: &CompilerCtxt<'tcx, B>, |
| ) -> Result<Allocation, B::Error> { |
| let ptr = Pointer::new(alloc_id.into(), Size::ZERO); |
| let scalar_ptr = Scalar::from_pointer(ptr, &cx.tcx); |
| let scalar_meta: Scalar = Scalar::from_target_usize(meta, &cx.tcx); |
| let mut allocation = Allocation::new(layout.size, layout.align.abi, AllocInit::Uninit, ()); |
| let ptr_size = cx.tcx.data_layout.pointer_size(); |
| allocation |
| .write_scalar(&cx.tcx, alloc_range(Size::ZERO, ptr_size), scalar_ptr) |
| .map_err(|e| B::Error::from_internal(e))?; |
| allocation |
| .write_scalar(&cx.tcx, alloc_range(ptr_size, scalar_meta.size()), scalar_meta) |
| .map_err(|e| B::Error::from_internal(e))?; |
| |
| Ok(allocation) |
| } |
| |
| pub fn try_new_indirect<'tcx, B: Bridge>( |
| alloc_id: AllocId, |
| cx: &CompilerCtxt<'tcx, B>, |
| ) -> ConstAllocation<'tcx> { |
| let alloc = cx.tcx.global_alloc(alloc_id).unwrap_memory(); |
| |
| alloc |
| } |
| |
| /// Creates an `Allocation` only from information within the `AllocRange`. |
| pub fn allocation_filter<'tcx, B: Bridge>( |
| alloc: &rustc_middle::mir::interpret::Allocation, |
| alloc_range: AllocRange, |
| tables: &mut Tables<'tcx, B>, |
| cx: &CompilerCtxt<'tcx, B>, |
| ) -> B::Allocation { |
| let mut bytes: Vec<Option<u8>> = alloc |
| .inspect_with_uninit_and_ptr_outside_interpreter( |
| alloc_range.start.bytes_usize()..alloc_range.end().bytes_usize(), |
| ) |
| .iter() |
| .copied() |
| .map(Some) |
| .collect(); |
| for (i, b) in bytes.iter_mut().enumerate() { |
| if !alloc.init_mask().get(Size::from_bytes(i + alloc_range.start.bytes_usize())) { |
| *b = None; |
| } |
| } |
| let mut ptrs = Vec::new(); |
| for (offset, prov) in alloc |
| .provenance() |
| .ptrs() |
| .iter() |
| .filter(|a| a.0 >= alloc_range.start && a.0 <= alloc_range.end()) |
| { |
| ptrs.push((offset.bytes_usize() - alloc_range.start.bytes_usize(), prov.alloc_id())); |
| } |
| |
| B::Allocation::new(bytes, ptrs, alloc.align.bytes(), alloc.mutability, tables, cx) |
| } |