Skip to content

Commit

Permalink
Auto merge of #55016 - oli-obk:vtables💥_vtables_everywhere, r=<try>
Browse files Browse the repository at this point in the history
Deduplicate some code and compile-time values around vtables

r? @RalfJung
  • Loading branch information
bors committed Oct 12, 2018
2 parents e9e27e6 + fce7dc2 commit 178f3ba
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 37 deletions.
10 changes: 7 additions & 3 deletions src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,7 @@ fn substitute_normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
fn vtable_methods<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>)
-> Lrc<Vec<Option<(DefId, &'tcx Substs<'tcx>)>>>
-> Lrc<Vec<Option<ty::Instance<'tcx>>>>
{
debug!("vtable_methods({:?})", trait_ref);

Expand Down Expand Up @@ -947,8 +947,12 @@ fn vtable_methods<'a, 'tcx>(
debug!("vtable_methods: predicates do not hold");
return None;
}

Some((def_id, substs))
Some(ty::Instance::resolve(
tcx,
ty::ParamEnv::reveal_all(),
def_id,
substs,
).unwrap())
})
}).collect()
)
Expand Down
20 changes: 11 additions & 9 deletions src/librustc/ty/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,10 @@ define_queries! { <'tcx>
},

Other {
/// List of methods on a vtable. The final vtable won't have holes where `None`, but will
/// simply skip methods that aren't object safe
[] fn vtable_methods: vtable_methods_node(ty::PolyTraitRef<'tcx>)
-> Lrc<Vec<Option<(DefId, &'tcx Substs<'tcx>)>>>,
-> Lrc<Vec<Option<ty::Instance<'tcx>>>>,
},

Codegen {
Expand All @@ -369,16 +371,16 @@ define_queries! { <'tcx>
-> Lrc<specialization_graph::Graph>,
[] fn is_object_safe: ObjectSafety(DefId) -> bool,

// Get the ParameterEnvironment for a given item; this environment
// will be in "user-facing" mode, meaning that it is suitabe for
// type-checking etc, and it does not normalize specializable
// associated types. This is almost always what you want,
// unless you are doing MIR optimizations, in which case you
// might want to use `reveal_all()` method to change modes.
/// Get the ParameterEnvironment for a given item; this environment
/// will be in "user-facing" mode, meaning that it is suitabe for
/// type-checking etc, and it does not normalize specializable
/// associated types. This is almost always what you want,
/// unless you are doing MIR optimizations, in which case you
/// might want to use `reveal_all()` method to change modes.
[] fn param_env: ParamEnv(DefId) -> ty::ParamEnv<'tcx>,

// Trait selection queries. These are best used by invoking `ty.moves_by_default()`,
// `ty.is_copy()`, etc, since that will prune the environment where possible.
/// Trait selection queries. These are best used by invoking `ty.moves_by_default()`,
/// `ty.is_copy()`, etc, since that will prune the environment where possible.
[] fn is_copy_raw: is_copy_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn is_sized_raw: is_sized_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
[] fn is_freeze_raw: is_freeze_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
Expand Down
8 changes: 6 additions & 2 deletions src/librustc_codegen_llvm/meth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ pub fn get_vtable(
let nullptr = C_null(Type::i8p(cx));

let (size, align) = cx.size_and_align_of(ty);
// /////////////////////////////////////////////////////////////////////////////////////////////
// If you touch this code, be sure to also make the corresponding changes to
// `get_vtable` in rust_mir/interpret/traits.rs
// /////////////////////////////////////////////////////////////////////////////////////////////
let mut components: Vec<_> = [
callee::get_fn(cx, monomorphize::resolve_drop_in_place(cx.tcx, ty)),
C_usize(cx, size.bytes()),
Expand All @@ -97,8 +101,8 @@ pub fn get_vtable(
let trait_ref = trait_ref.with_self_ty(tcx, ty);
let methods = tcx.vtable_methods(trait_ref);
let methods = methods.iter().cloned().map(|opt_mth| {
opt_mth.map_or(nullptr, |(def_id, substs)| {
callee::resolve_and_get_fn(cx, def_id, substs)
opt_mth.map_or(nullptr, |instance| {
callee::get_fn(cx, instance)
})
});
components.extend(methods);
Expand Down
7 changes: 1 addition & 6 deletions src/librustc_mir/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,12 +326,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
}
(_, &ty::Dynamic(ref data, _)) => {
// Initial cast from sized to dyn trait
let trait_ref = data.principal().unwrap().with_self_ty(
*self.tcx,
src_pointee_ty,
);
let trait_ref = self.tcx.erase_regions(&trait_ref);
let vtable = self.get_vtable(src_pointee_ty, trait_ref)?;
let vtable = self.get_vtable(src_pointee_ty, data.principal())?;
let ptr = self.read_value(src)?.to_scalar_ptr()?;
let val = Value::new_dyn_trait(ptr, vtable);
self.write_value(val, dest)
Expand Down
5 changes: 5 additions & 0 deletions src/librustc_mir/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use rustc::mir::interpret::{
EvalResult, EvalErrorKind,
truncate, sign_extend,
};
use rustc_data_structures::fx::FxHashMap;

use syntax::source_map::{self, Span};

Expand All @@ -50,6 +51,9 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> {

/// The virtual call stack.
pub(crate) stack: Vec<Frame<'mir, 'tcx, M::PointerTag>>,

/// A cache for deduplicating vtables
pub(super) vtables: FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), AllocId>,
}

/// A stack frame.
Expand Down Expand Up @@ -207,6 +211,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
param_env,
memory: Memory::new(tcx, memory_data),
stack: Vec::new(),
vtables: FxHashMap(),
}
}

Expand Down
40 changes: 28 additions & 12 deletions src/librustc_mir/interpret/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,36 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
pub fn get_vtable(
&mut self,
ty: Ty<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
) -> EvalResult<'tcx, Pointer<M::PointerTag>> {
debug!("get_vtable(trait_ref={:?})", trait_ref);
debug!("get_vtable(trait_ref={:?})", poly_trait_ref);

// FIXME: Cache this!
let (ty, poly_trait_ref) = self.tcx.erase_regions(&(ty, poly_trait_ref));

let layout = self.layout_of(trait_ref.self_ty())?;
if let Some(&vtable) = self.vtables.get(&(ty, poly_trait_ref)) {
return Ok(Pointer::from(vtable).with_default_tag());
}

let trait_ref = poly_trait_ref.map(|trait_ref| {
let trait_ref = trait_ref.with_self_ty(*self.tcx, ty);
self.tcx.erase_regions(&trait_ref)
});

let methods = trait_ref.map(|trait_ref| self.tcx.vtable_methods(trait_ref));

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();

let ptr_size = self.pointer_size();
let ptr_align = self.tcx.data_layout.pointer_align;
let methods = self.tcx.vtable_methods(trait_ref);
// /////////////////////////////////////////////////////////////////////////////////////////
// If you touch this code, be sure to also make the corresponding changes to
// `get_vtable` in rust_codegen_llvm/meth.rs
// /////////////////////////////////////////////////////////////////////////////////////////
let vtable = self.memory.allocate(
ptr_size * (3 + methods.len() as u64),
ptr_size * (3 + methods.as_ref().map_or(0, |m| m.len() as u64)),
ptr_align,
MemoryKind::Vtable,
)?;
Expand All @@ -54,16 +68,18 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
self.memory.write_ptr_sized(align_ptr, ptr_align,
Scalar::from_uint(align, ptr_size).into())?;

for (i, method) in methods.iter().enumerate() {
if let Some((def_id, substs)) = *method {
let instance = self.resolve(def_id, substs)?;
let fn_ptr = self.memory.create_fn_alloc(instance);
let method_ptr = vtable.offset(ptr_size * (3 + i as u64), &self)?;
self.memory.write_ptr_sized(method_ptr, ptr_align, Scalar::Ptr(fn_ptr).into())?;
if let Some(methods) = methods {
for (i, method) in methods.iter().enumerate() {
if let Some(instance) = *method {
let fn_ptr = self.memory.create_fn_alloc(instance);
let method_ptr = vtable.offset(ptr_size * (3 + i as u64), &self)?;
self.memory.write_ptr_sized(method_ptr, ptr_align, Scalar::Ptr(fn_ptr).into())?;
}
}
}

self.memory.mark_immutable(vtable.alloc_id)?;
assert!(self.vtables.insert((ty, poly_trait_ref), vtable.alloc_id).is_none());

Ok(vtable)
}
Expand Down
5 changes: 0 additions & 5 deletions src/librustc_mir/monomorphize/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,11 +914,6 @@ fn create_mono_items_for_vtable_methods<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Walk all methods of the trait, including those of its supertraits
let methods = tcx.vtable_methods(poly_trait_ref);
let methods = methods.iter().cloned().filter_map(|method| method)
.map(|(def_id, substs)| ty::Instance::resolve(
tcx,
ty::ParamEnv::reveal_all(),
def_id,
substs).unwrap())
.filter(|&instance| should_monomorphize_locally(tcx, &instance))
.map(|instance| create_fn_mono_item(instance));
output.extend(methods);
Expand Down

0 comments on commit 178f3ba

Please sign in to comment.