Skip to content

Commit

Permalink
translate closure shims using MIR
Browse files Browse the repository at this point in the history
  • Loading branch information
arielb1 committed Mar 18, 2017
1 parent a5e3c3d commit e1f3c67
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 321 deletions.
16 changes: 5 additions & 11 deletions src/librustc/ty/instance.rs
Expand Up @@ -34,10 +34,7 @@ pub enum InstanceDef<'tcx> {
// <Trait as Trait>::fn
Virtual(DefId, usize),
// <[mut closure] as FnOnce>::call_once
ClosureOnceShim {
call_once: DefId,
closure_did: DefId
},
ClosureOnceShim { call_once: DefId },
}

impl<'tcx> InstanceDef<'tcx> {
Expand All @@ -48,9 +45,8 @@ impl<'tcx> InstanceDef<'tcx> {
InstanceDef::FnPtrShim(def_id, _) |
InstanceDef::Virtual(def_id, _) |
InstanceDef::Intrinsic(def_id, ) |
InstanceDef::ClosureOnceShim {
call_once: def_id, closure_did: _
} => def_id
InstanceDef::ClosureOnceShim { call_once: def_id }
=> def_id
}
}

Expand Down Expand Up @@ -98,10 +94,8 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
InstanceDef::FnPtrShim(_, ty) => {
write!(f, " - shim({:?})", ty)
}
InstanceDef::ClosureOnceShim {
call_once: _, closure_did
} => {
write!(f, " - shim({:?})", closure_did)
InstanceDef::ClosureOnceShim { .. } => {
write!(f, " - shim")
}
}
}
Expand Down
121 changes: 94 additions & 27 deletions src/librustc_mir/shim.rs
Expand Up @@ -83,7 +83,25 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
None
)
}
_ => bug!("unknown shim kind")
ty::InstanceDef::ClosureOnceShim { call_once } => {
let fn_mut = tcx.lang_items.fn_mut_trait().unwrap();
let call_mut = tcx.global_tcx()
.associated_items(fn_mut)
.find(|it| it.kind == ty::AssociatedKind::Method)
.unwrap().def_id;

build_call_shim(
tcx,
&param_env,
call_once,
Adjustment::RefMut,
CallKind::Direct(call_mut),
None
)
}
ty::InstanceDef::Intrinsic(_) => {
bug!("creating shims from intrinsics ({:?}) is unsupported", instance)
}
};
debug!("make_shim({:?}) = {:?}", instance, result);

Expand All @@ -97,6 +115,7 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
enum Adjustment {
Identity,
Deref,
RefMut,
}

#[derive(Copy, Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -143,18 +162,37 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,

debug!("build_call_shim: sig={:?}", sig);

let local_decls = local_decls_for_sig(&sig);
let mut local_decls = local_decls_for_sig(&sig);
let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE };

let rcvr_l = Lvalue::Local(Local::new(1+0));

let return_block_id = BasicBlock::new(1);
let rcvr_arg = Local::new(1+0);
let rcvr_l = Lvalue::Local(rcvr_arg);
let mut statements = vec![];

let rcvr = match rcvr_adjustment {
Adjustment::Identity => Operand::Consume(rcvr_l),
Adjustment::Deref => Operand::Consume(Lvalue::Projection(
box Projection { base: rcvr_l, elem: ProjectionElem::Deref }
))
)),
Adjustment::RefMut => {
// let rcvr = &mut rcvr;
let re_erased = tcx.mk_region(ty::ReErased);
let ref_rcvr = local_decls.push(temp_decl(
Mutability::Not,
tcx.mk_ref(re_erased, ty::TypeAndMut {
ty: sig.inputs()[0],
mutbl: hir::Mutability::MutMutable
})
));
statements.push(Statement {
source_info: source_info,
kind: StatementKind::Assign(
Lvalue::Local(ref_rcvr),
Rvalue::Ref(re_erased, BorrowKind::Mut, rcvr_l)
)
});
Operand::Consume(Lvalue::Local(ref_rcvr))
}
};

let (callee, mut args) = match call_kind {
Expand Down Expand Up @@ -184,28 +222,57 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
}

let mut blocks = IndexVec::new();
blocks.push(BasicBlockData {
statements: vec![],
terminator: Some(Terminator {
source_info: source_info,
kind: TerminatorKind::Call {
func: callee,
args: args,
destination: Some((Lvalue::Local(RETURN_POINTER),
return_block_id)),
cleanup: None
let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| {
blocks.push(BasicBlockData {
statements,
terminator: Some(Terminator { source_info, kind }),
is_cleanup
})
};

let have_unwind = match (rcvr_adjustment, tcx.sess.no_landing_pads()) {
(Adjustment::RefMut, false) => true,
_ => false
};

// BB #0
block(&mut blocks, statements, TerminatorKind::Call {
func: callee,
args: args,
destination: Some((Lvalue::Local(RETURN_POINTER),
BasicBlock::new(1))),
cleanup: if have_unwind {
Some(BasicBlock::new(3))
} else {
None
}
}, false);

if let Adjustment::RefMut = rcvr_adjustment {
// BB #1 - drop for Self
block(&mut blocks, vec![], TerminatorKind::Drop {
location: Lvalue::Local(rcvr_arg),
target: BasicBlock::new(2),
unwind: if have_unwind {
Some(BasicBlock::new(4))
} else {
None
}
}),
is_cleanup: false
});
blocks.push(BasicBlockData {
statements: vec![],
terminator: Some(Terminator {
source_info: source_info,
kind: TerminatorKind::Return
}),
is_cleanup: false
});
}, false);
}
// BB #1/#2 - return
block(&mut blocks, vec![], TerminatorKind::Return, false);
if have_unwind {
// BB #3 - drop if closure panics
block(&mut blocks, vec![], TerminatorKind::Drop {
location: Lvalue::Local(rcvr_arg),
target: BasicBlock::new(4),
unwind: None
}, true);

// BB #4 - resume
block(&mut blocks, vec![], TerminatorKind::Resume, true);
}

let mut mir = Mir::new(
blocks,
Expand Down
173 changes: 7 additions & 166 deletions src/librustc_trans/callee.rs
Expand Up @@ -14,154 +14,29 @@
//! and methods are represented as just a fn ptr and not a full
//! closure.

use llvm::{self, ValueRef, get_params};
use llvm::{self, ValueRef};
use rustc::hir::def_id::DefId;
use rustc::ty::subst::{Substs, Subst};
use abi::{Abi, FnType};
use rustc::ty::subst::Substs;
use attributes;
use builder::Builder;
use common::{self, CrateContext};
use cleanup::CleanupScope;
use mir::lvalue::LvalueRef;
use monomorphize;
use consts;
use declare;
use value::Value;
use monomorphize::Instance;
use back::symbol_names::symbol_name;
use trans_item::TransItem;
use type_of;
use rustc::ty::{self, TypeFoldable};
use std::iter;

use mir::lvalue::Alignment;

fn trans_fn_once_adapter_shim<'a, 'tcx>(
ccx: &'a CrateContext<'a, 'tcx>,
def_id: DefId,
substs: ty::ClosureSubsts<'tcx>,
method_instance: Instance<'tcx>,
llreffn: ValueRef)
-> ValueRef
{
if let Some(&llfn) = ccx.instances().borrow().get(&method_instance) {
return llfn;
}

debug!("trans_fn_once_adapter_shim(def_id={:?}, substs={:?}, llreffn={:?})",
def_id, substs, Value(llreffn));

let tcx = ccx.tcx();

// Find a version of the closure type. Substitute static for the
// region since it doesn't really matter.
let closure_ty = tcx.mk_closure_from_closure_substs(def_id, substs);
let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), closure_ty);

// Make a version with the type of by-ref closure.
let sig = tcx.closure_type(def_id).subst(tcx, substs.substs);
let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
assert_eq!(sig.abi, Abi::RustCall);
let llref_fn_sig = tcx.mk_fn_sig(
iter::once(ref_closure_ty).chain(sig.inputs().iter().cloned()),
sig.output(),
sig.variadic,
sig.unsafety,
Abi::RustCall
);
let llref_fn_ty = tcx.mk_fn_ptr(ty::Binder(llref_fn_sig));
debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}",
llref_fn_ty);


// Make a version of the closure type with the same arguments, but
// with argument #0 being by value.
let sig = tcx.mk_fn_sig(
iter::once(closure_ty).chain(sig.inputs().iter().cloned()),
sig.output(),
sig.variadic,
sig.unsafety,
Abi::RustCall
);

let fn_ty = FnType::new(ccx, sig, &[]);
let llonce_fn_ty = tcx.mk_fn_ptr(ty::Binder(sig));

// Create the by-value helper.
let function_name = symbol_name(method_instance, ccx.shared());
let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
attributes::set_frame_pointer_elimination(ccx, lloncefn);

let orig_fn_ty = fn_ty;
let mut bcx = Builder::new_block(ccx, lloncefn, "entry-block");

// the first argument (`self`) will be the (by value) closure env.

let mut llargs = get_params(lloncefn);
let fn_ty = FnType::new(ccx, llref_fn_sig, &[]);
let self_idx = fn_ty.ret.is_indirect() as usize;
let env_arg = &orig_fn_ty.args[0];
let env = if env_arg.is_indirect() {
LvalueRef::new_sized_ty(llargs[self_idx], closure_ty, Alignment::AbiAligned)
} else {
let scratch = LvalueRef::alloca(&bcx, closure_ty, "self");
let mut llarg_idx = self_idx;
env_arg.store_fn_arg(&bcx, &mut llarg_idx, scratch.llval);
scratch
};

debug!("trans_fn_once_adapter_shim: env={:?}", env);
// Adjust llargs such that llargs[self_idx..] has the call arguments.
// For zero-sized closures that means sneaking in a new argument.
if env_arg.is_ignore() {
llargs.insert(self_idx, env.llval);
} else {
llargs[self_idx] = env.llval;
}

// Call the by-ref closure body with `self` in a cleanup scope,
// to drop `self` when the body returns, or in case it unwinds.
let self_scope = CleanupScope::schedule_drop_mem(&bcx, env);

let llret;
if let Some(landing_pad) = self_scope.landing_pad {
let normal_bcx = bcx.build_sibling_block("normal-return");
llret = bcx.invoke(llreffn, &llargs[..], normal_bcx.llbb(), landing_pad, None);
bcx = normal_bcx;
} else {
llret = bcx.call(llreffn, &llargs[..], None);
}
fn_ty.apply_attrs_callsite(llret);

if sig.output().is_never() {
bcx.unreachable();
} else {
self_scope.trans(&bcx);

if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() {
bcx.ret_void();
} else {
bcx.ret(llret);
}
}

ccx.instances().borrow_mut().insert(method_instance, lloncefn);

lloncefn
}

use rustc::ty::TypeFoldable;

/// Translates a reference to a fn/method item, monomorphizing and
/// inlining as it goes.
///
/// # Parameters
///
/// - `ccx`: the crate context
/// - `def_id`: def id of the fn or method item being referenced
/// - `substs`: values for each of the fn/method's parameters
fn do_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
instance: Instance<'tcx>)
-> ValueRef
/// - `instance`: the instance to be instantiated
pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
instance: Instance<'tcx>)
-> ValueRef
{
let tcx = ccx.tcx();

Expand Down Expand Up @@ -248,40 +123,6 @@ fn do_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
llfn
}

pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
instance: Instance<'tcx>)
-> ValueRef
{
match instance.def {
ty::InstanceDef::Intrinsic(_) => {
bug!("intrinsic {} getting reified", instance)
}
ty::InstanceDef::ClosureOnceShim { .. } => {
let closure_ty = instance.substs.type_at(0);
let (closure_def_id, closure_substs) = match closure_ty.sty {
ty::TyClosure(def_id, substs) => (def_id, substs),
_ => bug!("bad closure instance {:?}", instance)
};

trans_fn_once_adapter_shim(
ccx,
closure_def_id,
closure_substs,
instance,
do_get_fn(
ccx,
Instance::new(closure_def_id, closure_substs.substs)
)
)
}
ty::InstanceDef::FnPtrShim(..) |
ty::InstanceDef::Item(..) |
ty::InstanceDef::Virtual(..) => {
do_get_fn(ccx, instance)
}
}
}

pub fn resolve_and_get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>)
Expand Down

0 comments on commit e1f3c67

Please sign in to comment.