Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit bf80fec

Browse files
committed
translate function shims using MIR
1 parent ffee956 commit bf80fec

File tree

6 files changed

+199
-140
lines changed

6 files changed

+199
-140
lines changed

src/librustc_mir/shim.rs

Lines changed: 122 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,42 @@ use rustc::hir;
1212
use rustc::infer;
1313
use rustc::mir::*;
1414
use rustc::mir::transform::MirSource;
15-
use rustc::ty;
15+
use rustc::ty::{self, Ty};
1616
use rustc::ty::maps::Providers;
1717

1818
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
1919

20+
use syntax::abi::Abi;
2021
use syntax::ast;
22+
use syntax::codemap::DUMMY_SP;
2123
use syntax_pos::Span;
2224

2325
use std::cell::RefCell;
2426
use std::iter;
27+
use std::mem;
2528

2629
pub fn provide(providers: &mut Providers) {
2730
providers.mir_shims = make_shim;
2831
}
2932

30-
fn make_shim<'a, 'tcx>(_tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
33+
fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
3134
instance: ty::InstanceDef<'tcx>)
3235
-> &'tcx RefCell<Mir<'tcx>>
3336
{
34-
match instance {
37+
debug!("make_shim({:?})", instance);
38+
let result = match instance {
3539
ty::InstanceDef::Item(..) =>
3640
bug!("item {:?} passed to make_shim", instance),
37-
ty::InstanceDef::FnPtrShim(..) => unimplemented!()
38-
}
41+
ty::InstanceDef::FnPtrShim(_, ty) => {
42+
build_fn_ptr_shim(tcx, ty, instance.def_ty(tcx))
43+
}
44+
};
45+
debug!("make_shim({:?}) = {:?}", instance, result);
46+
47+
let result = tcx.alloc_mir(result);
48+
// Perma-borrow MIR from shims to prevent mutation.
49+
mem::forget(result.borrow());
50+
result
3951
}
4052

4153
fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>)
@@ -54,6 +66,111 @@ fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>)
5466
})).collect()
5567
}
5668

69+
70+
fn build_fn_ptr_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
71+
fn_ty: Ty<'tcx>,
72+
sig_ty: Ty<'tcx>)
73+
-> Mir<'tcx>
74+
{
75+
debug!("build_fn_ptr_shim(fn_ty={:?}, sig_ty={:?})", fn_ty, sig_ty);
76+
let trait_sig = match sig_ty.sty {
77+
ty::TyFnDef(_, _, fty) => tcx.erase_late_bound_regions(&fty),
78+
_ => bug!("unexpected type for shim {:?}", sig_ty)
79+
};
80+
81+
let self_ty = match trait_sig.inputs()[0].sty {
82+
ty::TyParam(..) => fn_ty,
83+
ty::TyRef(r, mt) => tcx.mk_ref(r, ty::TypeAndMut {
84+
ty: fn_ty,
85+
mutbl: mt.mutbl
86+
}),
87+
_ => bug!("unexpected self_ty {:?}", trait_sig),
88+
};
89+
90+
let fn_ptr_sig = match fn_ty.sty {
91+
ty::TyFnPtr(fty) |
92+
ty::TyFnDef(_, _, fty) =>
93+
tcx.erase_late_bound_regions_and_normalize(&fty),
94+
_ => bug!("non-fn-ptr {:?} in build_fn_ptr_shim", fn_ty)
95+
};
96+
97+
let sig = tcx.mk_fn_sig(
98+
[
99+
self_ty,
100+
tcx.intern_tup(fn_ptr_sig.inputs(), false)
101+
].iter().cloned(),
102+
fn_ptr_sig.output(),
103+
false,
104+
hir::Unsafety::Normal,
105+
Abi::RustCall,
106+
);
107+
108+
let local_decls = local_decls_for_sig(&sig);
109+
let source_info = SourceInfo {
110+
span: DUMMY_SP,
111+
scope: ARGUMENT_VISIBILITY_SCOPE
112+
};
113+
114+
let fn_ptr = Lvalue::Local(Local::new(1+0));
115+
let fn_ptr = match trait_sig.inputs()[0].sty {
116+
ty::TyParam(..) => fn_ptr,
117+
ty::TyRef(..) => Lvalue::Projection(box Projection {
118+
base: fn_ptr, elem: ProjectionElem::Deref
119+
}),
120+
_ => bug!("unexpected self_ty {:?}", trait_sig),
121+
};
122+
let fn_args = Local::new(1+1);
123+
124+
let return_block_id = BasicBlock::new(1);
125+
126+
// return = ADT(arg0, arg1, ...); return
127+
let start_block = BasicBlockData {
128+
statements: vec![],
129+
terminator: Some(Terminator {
130+
source_info: source_info,
131+
kind: TerminatorKind::Call {
132+
func: Operand::Consume(fn_ptr),
133+
args: fn_ptr_sig.inputs().iter().enumerate().map(|(i, ity)| {
134+
Operand::Consume(Lvalue::Projection(box Projection {
135+
base: Lvalue::Local(fn_args),
136+
elem: ProjectionElem::Field(
137+
Field::new(i), *ity
138+
)
139+
}))
140+
}).collect(),
141+
// FIXME: can we pass a Some destination for an uninhabited ty?
142+
destination: Some((Lvalue::Local(RETURN_POINTER),
143+
return_block_id)),
144+
cleanup: None
145+
}
146+
}),
147+
is_cleanup: false
148+
};
149+
let return_block = BasicBlockData {
150+
statements: vec![],
151+
terminator: Some(Terminator {
152+
source_info: source_info,
153+
kind: TerminatorKind::Return
154+
}),
155+
is_cleanup: false
156+
};
157+
158+
let mut mir = Mir::new(
159+
vec![start_block, return_block].into_iter().collect(),
160+
IndexVec::from_elem_n(
161+
VisibilityScopeData { span: DUMMY_SP, parent_scope: None }, 1
162+
),
163+
IndexVec::new(),
164+
sig.output(),
165+
local_decls,
166+
sig.inputs().len(),
167+
vec![],
168+
DUMMY_SP
169+
);
170+
mir.spread_arg = Some(fn_args);
171+
mir
172+
}
173+
57174
pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
58175
ctor_id: ast::NodeId,
59176
fields: &[hir::StructField],

src/librustc_trans/callee.rs

Lines changed: 7 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ use back::symbol_names::symbol_name;
3636
use trans_item::TransItem;
3737
use type_of;
3838
use rustc::ty::{self, Ty, TypeFoldable};
39-
use rustc::hir;
4039
use std::iter;
4140

4241
use syntax_pos::DUMMY_SP;
@@ -130,15 +129,14 @@ impl<'tcx> Callee<'tcx> {
130129
let method_ty = instance_ty(ccx.shared(), &instance);
131130
Callee::ptr(llfn, method_ty)
132131
}
133-
traits::VtableFnPointer(vtable_fn_pointer) => {
134-
let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
135-
let instance = Instance::new(def_id, substs);
136-
let llfn = trans_fn_pointer_shim(ccx, instance,
137-
trait_closure_kind,
138-
vtable_fn_pointer.fn_ty);
132+
traits::VtableFnPointer(data) => {
133+
let instance = ty::Instance {
134+
def: ty::InstanceDef::FnPtrShim(def_id, data.fn_ty),
135+
substs: substs,
136+
};
139137

140-
let method_ty = instance_ty(ccx.shared(), &instance);
141-
Callee::ptr(llfn, method_ty)
138+
let (llfn, ty) = get_fn(ccx, instance);
139+
Callee::ptr(llfn, ty)
142140
}
143141
traits::VtableObject(ref data) => {
144142
Callee {
@@ -363,124 +361,6 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
363361
lloncefn
364362
}
365363

366-
/// Translates an adapter that implements the `Fn` trait for a fn
367-
/// pointer. This is basically the equivalent of something like:
368-
///
369-
/// ```
370-
/// impl<'a> Fn(&'a int) -> &'a int for fn(&int) -> &int {
371-
/// extern "rust-abi" fn call(&self, args: (&'a int,)) -> &'a int {
372-
/// (*self)(args.0)
373-
/// }
374-
/// }
375-
/// ```
376-
///
377-
/// but for the bare function type given.
378-
fn trans_fn_pointer_shim<'a, 'tcx>(
379-
ccx: &'a CrateContext<'a, 'tcx>,
380-
method_instance: Instance<'tcx>,
381-
closure_kind: ty::ClosureKind,
382-
bare_fn_ty: Ty<'tcx>)
383-
-> ValueRef
384-
{
385-
let tcx = ccx.tcx();
386-
387-
// Normalize the type for better caching.
388-
let bare_fn_ty = tcx.normalize_associated_type(&bare_fn_ty);
389-
390-
// If this is an impl of `Fn` or `FnMut` trait, the receiver is `&self`.
391-
let is_by_ref = match closure_kind {
392-
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => true,
393-
ty::ClosureKind::FnOnce => false,
394-
};
395-
396-
let llfnpointer = match bare_fn_ty.sty {
397-
ty::TyFnDef(def_id, substs, _) => {
398-
// Function definitions have to be turned into a pointer.
399-
let llfn = Callee::def(ccx, def_id, substs).reify(ccx);
400-
if !is_by_ref {
401-
// A by-value fn item is ignored, so the shim has
402-
// the same signature as the original function.
403-
return llfn;
404-
}
405-
Some(llfn)
406-
}
407-
_ => None
408-
};
409-
410-
let bare_fn_ty_maybe_ref = if is_by_ref {
411-
tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), bare_fn_ty)
412-
} else {
413-
bare_fn_ty
414-
};
415-
416-
// Check if we already trans'd this shim.
417-
if let Some(&llval) = ccx.fn_pointer_shims().borrow().get(&bare_fn_ty_maybe_ref) {
418-
return llval;
419-
}
420-
421-
debug!("trans_fn_pointer_shim(bare_fn_ty={:?})",
422-
bare_fn_ty);
423-
424-
// Construct the "tuply" version of `bare_fn_ty`. It takes two arguments: `self`,
425-
// which is the fn pointer, and `args`, which is the arguments tuple.
426-
let sig = bare_fn_ty.fn_sig();
427-
let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
428-
assert_eq!(sig.unsafety, hir::Unsafety::Normal);
429-
assert_eq!(sig.abi, Abi::Rust);
430-
let tuple_input_ty = tcx.intern_tup(sig.inputs(), false);
431-
let sig = tcx.mk_fn_sig(
432-
[bare_fn_ty_maybe_ref, tuple_input_ty].iter().cloned(),
433-
sig.output(),
434-
false,
435-
hir::Unsafety::Normal,
436-
Abi::RustCall
437-
);
438-
let fn_ty = FnType::new(ccx, sig, &[]);
439-
let tuple_fn_ty = tcx.mk_fn_ptr(ty::Binder(sig));
440-
debug!("tuple_fn_ty: {:?}", tuple_fn_ty);
441-
442-
//
443-
let function_name = symbol_name(method_instance, ccx.shared());
444-
let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty);
445-
attributes::set_frame_pointer_elimination(ccx, llfn);
446-
//
447-
let bcx = Builder::new_block(ccx, llfn, "entry-block");
448-
449-
let mut llargs = get_params(llfn);
450-
451-
let self_arg = llargs.remove(fn_ty.ret.is_indirect() as usize);
452-
let llfnpointer = llfnpointer.unwrap_or_else(|| {
453-
// the first argument (`self`) will be ptr to the fn pointer
454-
if is_by_ref {
455-
bcx.load(self_arg, None)
456-
} else {
457-
self_arg
458-
}
459-
});
460-
461-
let callee = Callee {
462-
data: Fn(llfnpointer),
463-
ty: bare_fn_ty
464-
};
465-
let fn_ret = callee.ty.fn_ret();
466-
let fn_ty = callee.direct_fn_type(ccx, &[]);
467-
let llret = bcx.call(llfnpointer, &llargs, None);
468-
fn_ty.apply_attrs_callsite(llret);
469-
470-
if fn_ret.0.is_never() {
471-
bcx.unreachable();
472-
} else {
473-
if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() {
474-
bcx.ret_void();
475-
} else {
476-
bcx.ret(llret);
477-
}
478-
}
479-
480-
ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn);
481-
482-
llfn
483-
}
484364

485365
/// Translates a reference to a fn/method item, monomorphizing and
486366
/// inlining as it goes.

src/librustc_trans/collector.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -907,14 +907,12 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
907907
}
908908
}
909909
traits::VtableFnPointer(ref data) => {
910-
// If we know the destination of this fn-pointer, we'll have to make
911-
// sure that this destination actually gets instantiated.
912-
if let ty::TyFnDef(def_id, substs, _) = data.fn_ty.sty {
913-
// The destination of the pointer might be something that needs
914-
// further dispatching, such as a trait method, so we do that.
915-
do_static_dispatch(scx, def_id, substs)
916-
} else {
917-
StaticDispatchResult::Unknown
910+
StaticDispatchResult::Dispatched {
911+
instance: Instance {
912+
def: ty::InstanceDef::FnPtrShim(trait_method.def_id, data.fn_ty),
913+
substs: trait_ref.substs
914+
},
915+
fn_once_adjustment: None,
918916
}
919917
}
920918
// Trait object shims are always instantiated in-place, and as they are

src/test/codegen-units/item-collection/function-as-argument.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ fn main() {
2828

2929
//~ TRANS_ITEM fn function_as_argument::take_fn_once[0]<u32, &str, fn(u32, &str)>
3030
//~ TRANS_ITEM fn function_as_argument::function[0]<u32, &str>
31+
//~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0]<fn(u32, &str), (u32, &str)>
3132
take_fn_once(function, 0u32, "abc");
3233

3334
//~ TRANS_ITEM fn function_as_argument::take_fn_once[0]<char, f64, fn(char, f64)>
3435
//~ TRANS_ITEM fn function_as_argument::function[0]<char, f64>
36+
//~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0]<fn(char, f64), (char, f64)>
3537
take_fn_once(function, 'c', 0f64);
3638

3739
//~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0]<i32, ()>

src/test/codegen-units/item-collection/trait-method-as-argument.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,28 @@ fn take_foo_mut<T, F: FnMut(T) -> T>(mut f: F, arg: T) -> T {
4040
fn main() {
4141
//~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0]<u32, fn(u32) -> u32>
4242
//~ TRANS_ITEM fn trait_method_as_argument::{{impl}}[0]::foo[0]
43+
//~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0]<fn(u32) -> u32, (u32)>
4344
take_foo_once(Trait::foo, 0u32);
4445

4546
//~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0]<char, fn(char) -> char>
4647
//~ TRANS_ITEM fn trait_method_as_argument::Trait[0]::foo[0]<char>
48+
//~ TRANS_ITEM fn core::ops[0]::FnOnce[0]::call_once[0]<fn(char) -> char, (char)>
4749
take_foo_once(Trait::foo, 'c');
4850

4951
//~ TRANS_ITEM fn trait_method_as_argument::take_foo[0]<u32, fn(u32) -> u32>
52+
//~ TRANS_ITEM fn core::ops[0]::Fn[0]::call[0]<fn(u32) -> u32, (u32)>
5053
take_foo(Trait::foo, 0u32);
5154

5255
//~ TRANS_ITEM fn trait_method_as_argument::take_foo[0]<char, fn(char) -> char>
56+
//~ TRANS_ITEM fn core::ops[0]::Fn[0]::call[0]<fn(char) -> char, (char)>
5357
take_foo(Trait::foo, 'c');
5458

5559
//~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0]<u32, fn(u32) -> u32>
60+
//~ TRANS_ITEM fn core::ops[0]::FnMut[0]::call_mut[0]<fn(char) -> char, (char)>
5661
take_foo_mut(Trait::foo, 0u32);
5762

5863
//~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0]<char, fn(char) -> char>
64+
//~ TRANS_ITEM fn core::ops[0]::FnMut[0]::call_mut[0]<fn(u32) -> u32, (u32)>
5965
take_foo_mut(Trait::foo, 'c');
6066
}
6167

0 commit comments

Comments
 (0)