1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use attributes;
use llvm::{ValueRef, get_params};
use rustc::traits;
use callee::{Callee, CalleeData};
use common::*;
use builder::Builder;
use consts;
use declare;
use glue;
use machine;
use monomorphize::Instance;
use type_::Type;
use type_of::*;
use value::Value;
use rustc::ty;
const VTABLE_OFFSET: usize = 3;
pub fn get_virtual_method<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
llvtable: ValueRef,
vtable_index: usize) -> ValueRef {
debug!("get_virtual_method(vtable_index={}, llvtable={:?})",
vtable_index, Value(llvtable));
let ptr = bcx.load_nonnull(bcx.gepi(llvtable, &[vtable_index + VTABLE_OFFSET]), None);
bcx.set_invariant_load(ptr);
ptr
}
pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
callee: Callee<'tcx>)
-> ValueRef {
debug!("trans_object_shim({:?})", callee);
let function_name = match callee.ty.sty {
ty::TyFnDef(def_id, substs, _) => {
let instance = Instance::new(def_id, substs);
instance.symbol_name(ccx.shared())
}
_ => bug!()
};
let llfn = declare::define_internal_fn(ccx, &function_name, callee.ty);
attributes::set_frame_pointer_elimination(ccx, llfn);
let bcx = Builder::new_block(ccx, llfn, "entry-block");
let mut llargs = get_params(llfn);
let fn_ret = callee.ty.fn_ret();
let fn_ty = callee.direct_fn_type(ccx, &[]);
let fn_ptr = match callee.data {
CalleeData::Virtual(idx) => {
let fn_ptr = get_virtual_method(&bcx,
llargs.remove(fn_ty.ret.is_indirect() as usize + 1), idx);
let llty = fn_ty.llvm_type(bcx.ccx).ptr_to();
bcx.pointercast(fn_ptr, llty)
},
_ => bug!("trans_object_shim called with non-virtual callee"),
};
let llret = bcx.call(fn_ptr, &llargs, None);
fn_ty.apply_attrs_callsite(llret);
if fn_ret.0.is_never() {
bcx.unreachable();
} else {
if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() {
bcx.ret_void();
} else {
bcx.ret(llret);
}
}
llfn
}
pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
ty: ty::Ty<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>)
-> ValueRef
{
let tcx = ccx.tcx();
debug!("get_vtable(ty={:?}, trait_ref={:?})", ty, trait_ref);
if let Some(&val) = ccx.vtables().borrow().get(&(ty, trait_ref)) {
return val;
}
let nullptr = C_null(Type::nil(ccx).ptr_to());
let size_ty = sizing_type_of(ccx, ty);
let size = machine::llsize_of_alloc(ccx, size_ty);
let align = align_of(ccx, ty);
let mut components: Vec<_> = [
glue::get_drop_glue(ccx, ty),
C_uint(ccx, size),
C_uint(ccx, align)
].iter().cloned().collect();
if let Some(trait_ref) = trait_ref {
let trait_ref = trait_ref.with_self_ty(tcx, ty);
let methods = traits::get_vtable_methods(tcx, trait_ref).map(|opt_mth| {
opt_mth.map_or(nullptr, |(def_id, substs)| {
Callee::def(ccx, def_id, substs).reify(ccx)
})
});
components.extend(methods);
}
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((ty, trait_ref), vtable);
vtable
}