504 lines
19 KiB
Rust
Raw Normal View History

// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::rc::Rc;
use arena::TypedArena;
use back::link;
use llvm::{ValueRef, get_params};
use middle::def_id::DefId;
use middle::infer;
use middle::subst::{Subst, Substs};
use middle::subst;
use middle::traits::{self, ProjectionMode};
use trans::base::*;
use trans::build::*;
use trans::callee::{Callee, Virtual, ArgVals,
trans_fn_pointer_shim, trans_fn_ref_with_substs};
use trans::closure;
use trans::common::*;
use trans::consts;
use trans::datum::*;
use trans::debuginfo::DebugLoc;
use trans::declare;
use trans::expr;
use trans::glue;
use trans::machine;
use trans::type_::Type;
use trans::type_of::*;
use trans::value::Value;
2016-01-29 16:25:18 -08:00
use middle::ty::{self, Ty, TyCtxt, TypeFoldable};
use syntax::ast::{self, Name};
use syntax::attr;
use syntax::codemap::DUMMY_SP;
2015-07-31 00:04:06 -07:00
use rustc_front::hir;
2014-08-06 11:59:40 +02:00
// drop_glue pointer, size, align.
const VTABLE_OFFSET: usize = 3;
2014-08-06 11:59:40 +02:00
/// The main "translation" pass for methods. Generates code
/// for non-monomorphized methods only. Other methods will
/// be generated once they are invoked with specific type parameters,
/// see `trans::base::lval_static_fn()` or `trans::base::monomorphic_fn()`.
2014-03-06 18:47:24 +02:00
pub fn trans_impl(ccx: &CrateContext,
2015-09-20 04:50:30 +03:00
name: ast::Name,
impl_items: &[hir::ImplItem],
2015-07-31 00:04:06 -07:00
generics: &hir::Generics,
id: ast::NodeId) {
let _icx = push_ctxt("meth::trans_impl");
let tcx = ccx.tcx();
2013-03-15 15:24:24 -04:00
debug!("trans_impl(name={}, id={})", name, id);
2013-03-15 15:24:24 -04:00
// Both here and below with generic methods, be sure to recurse and look for
// items that we need to translate.
if !generics.ty_params.is_empty() {
return;
}
2015-01-31 12:20:46 -05:00
for impl_item in impl_items {
match impl_item.node {
2015-11-12 15:57:51 +01:00
hir::ImplItemKind::Method(ref sig, ref body) => {
if sig.generics.ty_params.is_empty() {
let trans_everywhere = attr::requests_inline(&impl_item.attrs);
for (ref ccx, is_origin) in ccx.maybe_iter(trans_everywhere) {
let llfn = get_item_val(ccx, impl_item.id);
let empty_substs = tcx.mk_substs(Substs::trans_empty());
trans_fn(ccx,
&sig.decl,
body,
llfn,
empty_substs,
impl_item.id,
&impl_item.attrs);
update_linkage(ccx,
llfn,
Some(impl_item.id),
if is_origin { OriginalTranslation } else { InlinedCopy });
}
}
}
_ => {}
}
2012-01-02 12:21:44 +01:00
}
}
/// Compute the appropriate callee, give na method's ID, trait ID,
/// substitutions and a Vtable for that trait.
pub fn callee_for_trait_impl<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
method_id: DefId,
substs: &'tcx subst::Substs<'tcx>,
trait_id: DefId,
method_ty: Ty<'tcx>,
vtable: traits::Vtable<'tcx, ()>)
-> Callee<'tcx> {
let _icx = push_ctxt("meth::callee_for_trait_impl");
match vtable {
traits::VtableImpl(vtable_impl) => {
let impl_did = vtable_impl.impl_def_id;
let mname = ccx.tcx().item_name(method_id);
// create a concatenated set of substitutions which includes
// those from the impl and those from the method:
let impl_substs = vtable_impl.substs.with_method_from(&substs);
let substs = ccx.tcx().mk_substs(impl_substs);
2016-03-11 15:51:35 -08:00
let mth = get_impl_method(ccx.tcx(), impl_did, substs, mname);
// Translate the function, bypassing Callee::def.
// That is because default methods have the same ID as the
// trait method used to look up the impl method that ended
// up here, so calling Callee::def would infinitely recurse.
Callee::ptr(trans_fn_ref_with_substs(ccx, mth.method.def_id,
Some(method_ty), mth.substs))
}
traits::VtableClosure(vtable_closure) => {
// The substitutions should have no type parameters remaining
// after passing through fulfill_obligation
let trait_closure_kind = ccx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
let llfn = closure::trans_closure_method(ccx,
vtable_closure.closure_def_id,
vtable_closure.substs,
trait_closure_kind);
let fn_ptr_ty = match method_ty.sty {
ty::TyFnDef(_, _, fty) => ccx.tcx().mk_ty(ty::TyFnPtr(fty)),
_ => unreachable!("expected fn item type, found {}",
method_ty)
};
Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
}
traits::VtableFnPointer(fn_ty) => {
let trait_closure_kind = ccx.tcx().lang_items.fn_trait_kind(trait_id).unwrap();
let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty);
let fn_ptr_ty = match method_ty.sty {
ty::TyFnDef(_, _, fty) => ccx.tcx().mk_ty(ty::TyFnPtr(fty)),
_ => unreachable!("expected fn item type, found {}",
method_ty)
};
Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty))
}
traits::VtableObject(ref data) => {
Callee {
data: Virtual(traits::get_vtable_index_of_object_method(
ccx.tcx(), data, method_id)),
ty: method_ty
}
}
traits::VtableBuiltin(..) |
2015-02-07 14:24:34 +01:00
traits::VtableDefaultImpl(..) |
traits::VtableParam(..) => {
ccx.sess().bug(
2015-06-18 20:25:05 +03:00
&format!("resolved vtable bad vtable {:?} in trans",
vtable));
}
}
}
/// Extracts a method from a trait object's vtable, at the
/// specified index, and casts it to the given type.
pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
llvtable: ValueRef,
vtable_index: usize,
method_ty: Ty<'tcx>)
-> Datum<'tcx, Rvalue> {
let _icx = push_ctxt("meth::get_virtual_method");
let ccx = bcx.ccx();
// Load the data pointer from the object.
debug!("get_virtual_method(callee_ty={}, vtable_index={}, llvtable={:?})",
method_ty, vtable_index, Value(llvtable));
Pass fat pointers in two immediate arguments This has a number of advantages compared to creating a copy in memory and passing a pointer. The obvious one is that we don't have to put the data into memory but can keep it in registers. Since we're currently passing a pointer anyway (instead of using e.g. a known offset on the stack, which is what the `byval` attribute would achieve), we only use a single additional register for each fat pointer, but save at least two pointers worth of stack in exchange (sometimes more because more than one copy gets eliminated). On archs that pass arguments on the stack, we save a pointer worth of stack even without considering the omitted copies. Additionally, LLVM can optimize the code a lot better, to a large degree due to the fact that lots of copies are gone or can be optimized away. Additionally, we can now emit attributes like nonnull on the data and/or vtable pointers contained in the fat pointer, potentially allowing for even more optimizations. This results in LLVM passes being about 3-7% faster (depending on the crate), and the resulting code is also a few percent smaller, for example: text data filename 5671479 3941461 before/librustc-d8ace771.so 5447663 3905745 after/librustc-d8ace771.so 1944425 2394024 before/libstd-d8ace771.so 1896769 2387610 after/libstd-d8ace771.so I had to remove a call in the backtrace-debuginfo test, because LLVM can now merge the tails of some blocks when optimizations are turned on, which can't correctly preserve line info. Fixes #22924 Cc #22891 (at least for fat pointers the code is good now)
2015-06-18 23:57:40 +02:00
let mptr = Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET]));
// Replace the self type (&Self or Box<Self>) with an opaque pointer.
if let ty::TyFnDef(_, _, fty) = method_ty.sty {
let opaque_ty = opaque_method_ty(ccx.tcx(), fty);
immediate_rvalue(PointerCast(bcx, mptr, type_of(ccx, opaque_ty)), opaque_ty)
} else {
immediate_rvalue(mptr, method_ty)
}
}
/// Generate a shim function that allows an object type like `SomeTrait` to
/// implement the type `SomeTrait`. Imagine a trait definition:
///
2015-12-02 09:06:28 +00:00
/// trait SomeTrait { fn get(&self) -> i32; ... }
///
/// And a generic bit of code:
///
/// fn foo<T:SomeTrait>(t: &T) {
/// let x = SomeTrait::get;
/// x(t)
/// }
///
/// What is the value of `x` when `foo` is invoked with `T=SomeTrait`?
/// The answer is that it is a shim function generated by this routine:
///
2015-12-02 09:06:28 +00:00
/// fn shim(t: &SomeTrait) -> i32 {
/// // ... call t.get() virtually ...
/// }
///
/// In fact, all virtual calls can be thought of as normal trait calls
/// that go through this shim function.
pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
method_ty: Ty<'tcx>,
vtable_index: usize)
-> Datum<'tcx, Rvalue> {
let _icx = push_ctxt("trans_object_shim");
let tcx = ccx.tcx();
debug!("trans_object_shim(vtable_index={}, method_ty={:?})",
vtable_index,
method_ty);
let ret_ty = tcx.erase_late_bound_regions(&method_ty.fn_ret());
let ret_ty = infer::normalize_associated_type(tcx, &ret_ty);
let shim_fn_ty = match method_ty.sty {
ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)),
_ => unreachable!("expected fn item type, found {}", method_ty)
};
//
let function_name = link::mangle_internal_name_by_type_and_seq(ccx, shim_fn_ty, "object_shim");
let llfn = declare::define_internal_rust_fn(ccx, &function_name, shim_fn_ty);
let empty_substs = tcx.mk_substs(Substs::trans_empty());
let (block_arena, fcx): (TypedArena<_>, FunctionContext);
block_arena = TypedArena::new();
fcx = new_fn_ctxt(ccx,
llfn,
ast::DUMMY_NODE_ID,
false,
ret_ty,
empty_substs,
None,
&block_arena);
let mut bcx = init_function(&fcx, false, ret_ty);
let llargs = get_params(fcx.llfn);
Pass fat pointers in two immediate arguments This has a number of advantages compared to creating a copy in memory and passing a pointer. The obvious one is that we don't have to put the data into memory but can keep it in registers. Since we're currently passing a pointer anyway (instead of using e.g. a known offset on the stack, which is what the `byval` attribute would achieve), we only use a single additional register for each fat pointer, but save at least two pointers worth of stack in exchange (sometimes more because more than one copy gets eliminated). On archs that pass arguments on the stack, we save a pointer worth of stack even without considering the omitted copies. Additionally, LLVM can optimize the code a lot better, to a large degree due to the fact that lots of copies are gone or can be optimized away. Additionally, we can now emit attributes like nonnull on the data and/or vtable pointers contained in the fat pointer, potentially allowing for even more optimizations. This results in LLVM passes being about 3-7% faster (depending on the crate), and the resulting code is also a few percent smaller, for example: text data filename 5671479 3941461 before/librustc-d8ace771.so 5447663 3905745 after/librustc-d8ace771.so 1944425 2394024 before/libstd-d8ace771.so 1896769 2387610 after/libstd-d8ace771.so I had to remove a call in the backtrace-debuginfo test, because LLVM can now merge the tails of some blocks when optimizations are turned on, which can't correctly preserve line info. Fixes #22924 Cc #22891 (at least for fat pointers the code is good now)
2015-06-18 23:57:40 +02:00
let self_idx = fcx.arg_offset();
let llself = llargs[self_idx];
let llvtable = llargs[self_idx + 1];
debug!("trans_object_shim: llself={:?}, llvtable={:?}",
Value(llself), Value(llvtable));
assert!(!fcx.needs_ret_allocas);
let dest =
fcx.llretslotptr.get().map(
|_| expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot")));
debug!("trans_object_shim: method_offset_in_vtable={}",
vtable_index);
let callee = Callee {
data: Virtual(vtable_index),
ty: method_ty
};
bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[self_idx..]), dest).bcx;
finish_fn(&fcx, bcx, ret_ty, DebugLoc::None);
immediate_rvalue(llfn, shim_fn_ty)
}
/// Creates a returns a dynamic vtable for the given type and vtable origin.
/// This is used only for objects.
///
/// The `trait_ref` encodes the erased self type. Hence if we are
/// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
/// `trait_ref` would map `T:Trait`.
pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>)
-> ValueRef
{
let tcx = ccx.tcx();
let _icx = push_ctxt("meth::get_vtable");
2015-06-18 20:25:05 +03:00
debug!("get_vtable(trait_ref={:?})", trait_ref);
// Check the cache.
match ccx.vtables().borrow().get(&trait_ref) {
2014-03-20 19:49:20 -07:00
Some(&val) => { return val }
None => { }
}
// Not in the cache. Build it.
let methods = traits::supertraits(tcx, trait_ref.clone()).flat_map(|trait_ref| {
let vtable = fulfill_obligation(ccx, DUMMY_SP, trait_ref.clone());
match vtable {
2015-01-24 14:17:24 +01:00
// Should default trait error here?
2015-02-07 14:24:34 +01:00
traits::VtableDefaultImpl(_) |
traits::VtableBuiltin(_) => {
Vec::new().into_iter()
}
traits::VtableImpl(
traits::VtableImplData {
impl_def_id: id,
substs,
nested: _ }) => {
let nullptr = C_null(Type::nil(ccx).ptr_to());
get_vtable_methods(ccx, id, substs)
.into_iter()
.map(|opt_mth| {
match opt_mth {
Some(mth) => {
trans_fn_ref_with_substs(ccx,
mth.method.def_id,
None,
2016-03-11 15:51:35 -08:00
&mth.substs).val
}
None => nullptr
}
})
.collect::<Vec<_>>()
.into_iter()
}
traits::VtableClosure(
traits::VtableClosureData {
closure_def_id,
substs,
nested: _ }) => {
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap();
let llfn = closure::trans_closure_method(ccx,
closure_def_id,
substs,
trait_closure_kind);
vec![llfn].into_iter()
}
traits::VtableFnPointer(bare_fn_ty) => {
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap();
vec![trans_fn_pointer_shim(ccx, trait_closure_kind, bare_fn_ty)].into_iter()
}
traits::VtableObject(ref data) => {
// this would imply that the Self type being erased is
// an object type; this cannot happen because we
// cannot cast an unsized type into a trait object
tcx.sess.bug(
2015-06-18 20:25:05 +03:00
&format!("cannot get vtable for an object type: {:?}",
data));
}
traits::VtableParam(..) => {
tcx.sess.bug(
2015-06-18 20:25:05 +03:00
&format!("resolved vtable for {:?} to bad vtable {:?} in trans",
trait_ref,
vtable));
}
}
2014-04-10 14:04:45 +03:00
});
let size_ty = sizing_type_of(ccx, trait_ref.self_ty());
2014-08-06 11:59:40 +02:00
let size = machine::llsize_of_alloc(ccx, size_ty);
let align = align_of(ccx, trait_ref.self_ty());
2014-08-06 11:59:40 +02:00
let components: Vec<_> = vec![
// Generate a destructor for the vtable.
glue::get_drop_glue(ccx, trait_ref.self_ty()),
C_uint(ccx, size),
C_uint(ccx, align)
].into_iter().chain(methods).collect();
let vtable_const = C_struct(ccx, &components, false);
let align = machine::llalign_of_pref(ccx, val_ty(vtable_const));
let vtable = consts::addr_of(ccx, vtable_const, align, "vtable");
ccx.vtables().borrow_mut().insert(trait_ref, vtable);
2014-04-10 14:04:45 +03:00
vtable
}
pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
impl_id: DefId,
substs: &'tcx subst::Substs<'tcx>)
-> Vec<Option<ImplMethod<'tcx>>>
{
let tcx = ccx.tcx();
debug!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id, substs);
let trt_id = match tcx.impl_trait_ref(impl_id) {
Some(t_id) => t_id.def_id,
2014-03-05 16:36:01 +02:00
None => ccx.sess().bug("make_impl_vtable: don't know how to \
make a vtable for a type impl!")
};
tcx.populate_implementations_for_trait_if_necessary(trt_id);
let trait_item_def_ids = tcx.trait_item_def_ids(trt_id);
trait_item_def_ids
.iter()
// Filter out non-method items.
.filter_map(|item_def_id| {
match *item_def_id {
ty::MethodTraitItemId(def_id) => Some(def_id),
_ => None,
}
})
// Now produce pointers for each remaining method. If the
// method could never be called from this object, just supply
// null.
.map(|trait_method_def_id| {
debug!("get_vtable_methods: trait_method_def_id={:?}",
2015-06-18 20:25:05 +03:00
trait_method_def_id);
let trait_method_type = match tcx.impl_or_trait_item(trait_method_def_id) {
ty::MethodTraitItem(m) => m,
_ => ccx.sess().bug("should be a method, not other assoc item"),
};
let name = trait_method_type.name;
// Some methods cannot be called on an object; skip those.
if !traits::is_vtable_safe_method(tcx, trt_id, &trait_method_type) {
debug!("get_vtable_methods: not vtable safe");
return None;
}
debug!("get_vtable_methods: trait_method_type={:?}",
2015-06-18 20:25:05 +03:00
trait_method_type);
// The substitutions we have are on the impl, so we grab
// the method type from the impl to substitute into.
2016-03-11 15:51:35 -08:00
let mth = get_impl_method(tcx, impl_id, substs, name);
debug!("get_vtable_methods: mth={:?}", mth);
// If this is a default method, it's possible that it
// relies on where clauses that do not hold for this
// particular set of type parameters. Note that this
// method could then never be called, so we do not want to
// try and trans it, in that case. Issue #23435.
if mth.is_provided {
2016-03-11 15:51:35 -08:00
let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs);
if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
debug!("get_vtable_methods: predicates do not hold");
return None;
}
}
Some(mth)
})
.collect()
}
/// Replace the self type (&Self or Box<Self>) with an opaque pointer.
2016-02-29 23:36:51 +00:00
fn opaque_method_ty<'tcx>(tcx: &TyCtxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>)
-> Ty<'tcx> {
let mut inputs = method_ty.sig.0.inputs.clone();
inputs[0] = tcx.mk_mut_ptr(tcx.mk_mach_int(ast::IntTy::I8));
tcx.mk_fn_ptr(ty::BareFnTy {
unsafety: method_ty.unsafety,
abi: method_ty.abi,
sig: ty::Binder(ty::FnSig {
inputs: inputs,
output: method_ty.sig.0.output,
variadic: method_ty.sig.0.variadic,
}),
})
}
#[derive(Debug)]
pub struct ImplMethod<'tcx> {
pub method: Rc<ty::Method<'tcx>>,
2016-03-11 15:51:35 -08:00
pub substs: &'tcx Substs<'tcx>,
pub is_provided: bool
}
/// Locates the applicable definition of a method, given its name.
2016-03-08 16:09:39 -08:00
pub fn get_impl_method<'tcx>(tcx: &TyCtxt<'tcx>,
impl_def_id: DefId,
2016-03-11 15:51:35 -08:00
substs: &'tcx Substs<'tcx>,
name: Name)
-> ImplMethod<'tcx>
{
assert!(!substs.types.needs_infer());
let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
let trait_def = tcx.lookup_trait_def(trait_def_id);
let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any);
match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() {
Some(node_item) => {
ImplMethod {
method: node_item.item,
substs: traits::translate_substs(&infcx, impl_def_id, substs, node_item.node),
is_provided: node_item.node.is_from_trait(),
}
}
None => {
tcx.sess.bug(&format!("method {:?} not found in {:?}", name, impl_def_id))
}
}
}