Update other codegens to use tcx managed vtable allocations.
This commit is contained in:
@@ -1,29 +1,14 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use rustc_middle::mir::interpret::{
|
||||
AllocError, InterpError, InterpResult, Pointer, PointerArithmetic, Scalar,
|
||||
UndefinedBehaviorInfo, UnsupportedOpInfo,
|
||||
};
|
||||
use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic, Scalar};
|
||||
use rustc_middle::ty::{
|
||||
self, Instance, Ty, VtblEntry, COMMON_VTABLE_ENTRIES, COMMON_VTABLE_ENTRIES_ALIGN,
|
||||
self, Ty, COMMON_VTABLE_ENTRIES, COMMON_VTABLE_ENTRIES_ALIGN,
|
||||
COMMON_VTABLE_ENTRIES_DROPINPLACE, COMMON_VTABLE_ENTRIES_SIZE,
|
||||
};
|
||||
use rustc_target::abi::{Align, LayoutOf, Size};
|
||||
use rustc_target::abi::{Align, Size};
|
||||
|
||||
use super::alloc_range;
|
||||
use super::util::ensure_monomorphic_enough;
|
||||
use super::{Allocation, FnVal, InterpCx, Machine};
|
||||
|
||||
fn vtable_alloc_error_to_interp_error<'tcx>(error: AllocError) -> InterpError<'tcx> {
|
||||
match error {
|
||||
AllocError::ReadPointerAsBytes => {
|
||||
InterpError::Unsupported(UnsupportedOpInfo::ReadPointerAsBytes)
|
||||
}
|
||||
AllocError::InvalidUninitBytes(_info) => {
|
||||
InterpError::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(None))
|
||||
}
|
||||
}
|
||||
}
|
||||
use super::{FnVal, InterpCx, Machine};
|
||||
|
||||
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
|
||||
@@ -45,79 +30,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
ensure_monomorphic_enough(*self.tcx, ty)?;
|
||||
ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;
|
||||
|
||||
if let Some(&vtable) = self.vtables.get(&(ty, poly_trait_ref)) {
|
||||
// This means we guarantee that there are no duplicate vtables, we will
|
||||
// always use the same vtable for the same (Type, Trait) combination.
|
||||
// That's not what happens in rustc, but emulating per-crate deduplication
|
||||
// does not sound like it actually makes anything any better.
|
||||
return Ok(vtable);
|
||||
}
|
||||
let vtable_allocation = self.tcx.vtable_allocation(ty, poly_trait_ref);
|
||||
|
||||
let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref {
|
||||
let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty);
|
||||
let trait_ref = self.tcx.erase_regions(trait_ref);
|
||||
|
||||
self.tcx.vtable_entries(trait_ref)
|
||||
} else {
|
||||
COMMON_VTABLE_ENTRIES
|
||||
};
|
||||
|
||||
let layout = self.layout_of(ty)?;
|
||||
assert!(!layout.is_unsized(), "can't create a vtable for an unsized type");
|
||||
let size = layout.size.bytes();
|
||||
let align = layout.align.abi.bytes();
|
||||
|
||||
let tcx = *self.tcx;
|
||||
let ptr_size = self.pointer_size();
|
||||
let ptr_align = tcx.data_layout.pointer_align.abi;
|
||||
// /////////////////////////////////////////////////////////////////////////////////////////
|
||||
// If you touch this code, be sure to also make the corresponding changes to
|
||||
// `get_vtable` in `rust_codegen_llvm/meth.rs`.
|
||||
// /////////////////////////////////////////////////////////////////////////////////////////
|
||||
let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap();
|
||||
let mut vtable = Allocation::uninit(vtable_size, ptr_align);
|
||||
|
||||
// No need to do any alignment checks on the memory accesses below, because we know the
|
||||
// allocation is correctly aligned as we created it above. Also we're only offsetting by
|
||||
// multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
|
||||
let scalars = vtable_entries
|
||||
.iter()
|
||||
.map(|entry| -> InterpResult<'tcx, _> {
|
||||
match entry {
|
||||
VtblEntry::MetadataDropInPlace => {
|
||||
let instance = Instance::resolve_drop_in_place(tcx, ty);
|
||||
let fn_alloc_id = tcx.create_fn_alloc(instance);
|
||||
let fn_ptr = Pointer::from(fn_alloc_id);
|
||||
Ok(Some(fn_ptr.into()))
|
||||
}
|
||||
VtblEntry::MetadataSize => Ok(Some(Scalar::from_uint(size, ptr_size).into())),
|
||||
VtblEntry::MetadataAlign => Ok(Some(Scalar::from_uint(align, ptr_size).into())),
|
||||
VtblEntry::Vacant => Ok(None),
|
||||
VtblEntry::Method(def_id, substs) => {
|
||||
// Prepare the fn ptr we write into the vtable.
|
||||
let instance =
|
||||
Instance::resolve_for_vtable(tcx, self.param_env, *def_id, substs)
|
||||
.ok_or_else(|| err_inval!(TooGeneric))?;
|
||||
let fn_alloc_id = tcx.create_fn_alloc(instance);
|
||||
let fn_ptr = Pointer::from(fn_alloc_id);
|
||||
Ok(Some(fn_ptr.into()))
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
for (idx, scalar) in scalars.into_iter().enumerate() {
|
||||
if let Some(scalar) = scalar {
|
||||
let idx: u64 = u64::try_from(idx).unwrap();
|
||||
vtable
|
||||
.write_scalar(self, alloc_range(ptr_size * idx, ptr_size), scalar)
|
||||
.map_err(vtable_alloc_error_to_interp_error)?;
|
||||
}
|
||||
}
|
||||
|
||||
let vtable_id = tcx.create_memory_alloc(tcx.intern_const_alloc(vtable));
|
||||
let vtable_ptr = self.memory.global_base_pointer(Pointer::from(vtable_id))?;
|
||||
|
||||
assert!(self.vtables.insert((ty, poly_trait_ref), vtable_ptr).is_none());
|
||||
let vtable_ptr = self.memory.global_base_pointer(Pointer::from(vtable_allocation))?;
|
||||
|
||||
Ok(vtable_ptr)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user