1 // Determines the ways in which a generic function body depends
2 // on its type parameters. Used to aggressively reuse compiled
3 // function bodies for different types.
5 // This unfortunately depends on quite a bit of knowledge about the
6 // details of the language semantics, and is likely to accidentally go
7 // out of sync when something is changed. It could be made more
8 // powerful by distinguishing between functions that only need to know
9 // the size and alignment of a type, and those that also use its
10 // drop/take glue. But this would increase the fragility of the code
11 // to a ridiculous level, and probably not catch all that many extra
12 // opportunities for reuse.
14 // (An other approach to doing what this code does is to instrument
15 // the translation code to set flags whenever it does something like
16 // alloca a type or get a tydesc. This would not duplicate quite as
17 // much information, but have the disadvantage of being very
20 use std::map::HashMap;
22 use std::list::{List, Cons, Nil};
23 use metadata::csearch;
24 use syntax::ast::*, syntax::ast_util, syntax::visit;
28 type type_uses = uint; // Bitmask
29 const use_repr: uint = 1u; /* Dependency on size/alignment/mode and
31 const use_tydesc: uint = 2u; /* Takes the tydesc, or compares */
33 type ctx = {ccx: @crate_ctxt,
34 uses: ~[mut type_uses]};
36 fn type_uses_for(ccx: @crate_ctxt, fn_id: def_id, n_tps: uint)
38 match ccx.type_use_cache.find(fn_id) {
39 Some(uses) => return uses,
43 let fn_id_loc = if fn_id.crate == local_crate {
46 inline::maybe_instantiate_inline(ccx, fn_id, true)
49 // Conservatively assume full use for recursive loops
50 ccx.type_use_cache.insert(fn_id, vec::from_elem(n_tps, 3u));
52 let cx = {ccx: ccx, uses: vec::to_mut(vec::from_elem(n_tps, 0u))};
53 match ty::get(ty::lookup_item_type(cx.ccx.tcx, fn_id).ty).sty {
54 ty::ty_fn(ref fn_ty) => {
55 for vec::each(fn_ty.sig.inputs) |arg| {
56 match ty::resolved_mode(ccx.tcx, arg.mode) {
57 by_val | by_move | by_copy => {
58 type_needs(cx, use_repr, arg.ty);
67 if fn_id_loc.crate != local_crate {
68 let uses = vec::from_mut(copy cx.uses);
69 ccx.type_use_cache.insert(fn_id, uses);
72 let map_node = match ccx.tcx.items.find(fn_id_loc.node) {
74 None => ccx.sess.bug(fmt!("type_uses_for: unbound item ID %?",
78 ast_map::node_item(@{node: item_fn(_, _, _, body), _}, _) |
79 ast_map::node_method(@{body, _}, _, _) => {
80 handle_body(cx, body);
82 ast_map::node_trait_method(*) => {
83 // This will be a static trait method. For now, we just assume
84 // it fully depends on all of the type information. (Doing
85 // otherwise would require finding the actual implementation).
86 for uint::range(0u, n_tps) |n| { cx.uses[n] |= use_repr|use_tydesc;}
88 ast_map::node_variant(_, _, _) => {
89 for uint::range(0u, n_tps) |n| { cx.uses[n] |= use_repr;}
91 ast_map::node_foreign_item(i@@{node: foreign_item_fn(*), _},
93 if abi == foreign_abi_rust_intrinsic {
94 let flags = match cx.ccx.sess.str_of(i.ident) {
95 ~"size_of" | ~"pref_align_of" | ~"min_align_of" |
96 ~"init" | ~"reinterpret_cast" |
97 ~"move_val" | ~"move_val_init" => use_repr,
99 ~"get_tydesc" | ~"needs_drop" => use_tydesc,
101 ~"atomic_cxchg" | ~"atomic_cxchg_acq"|
102 ~"atomic_cxchg_rel"| ~"atomic_xchg" |
103 ~"atomic_xadd" | ~"atomic_xsub" |
104 ~"atomic_xchg_acq" | ~"atomic_xadd_acq" |
105 ~"atomic_xsub_acq" | ~"atomic_xchg_rel" |
106 ~"atomic_xadd_rel" | ~"atomic_xsub_rel" => 0,
108 ~"visit_tydesc" | ~"forget" | ~"addr_of" |
109 ~"frame_address" | ~"morestack_addr" => 0,
111 ~"sqrtf32" | ~"sqrtf64" | ~"powif32" | ~"powif64" |
112 ~"sinf32" | ~"sinf64" | ~"cosf32" | ~"cosf64" |
113 ~"powf32" | ~"powf64" | ~"expf32" | ~"expf64" |
114 ~"exp2f32" | ~"exp2f64" | ~"logf32" | ~"logf64" |
115 ~"log10f32"| ~"log10f64"| ~"log2f32" | ~"log2f64" |
116 ~"fmaf32" | ~"fmaf64" | ~"fabsf32" | ~"fabsf64" |
117 ~"floorf32"| ~"floorf64"| ~"ceilf32" | ~"ceilf64" |
118 ~"truncf32"| ~"truncf64" => 0,
120 // would be cool to make these an enum instead of strings!
121 _ => fail ~"unknown intrinsic in type_use"
123 for uint::range(0u, n_tps) |n| { cx.uses[n] |= flags;}
126 ast_map::node_dtor(_, dtor, _, _) => {
127 handle_body(cx, dtor.node.body);
129 _ => fail ~"unknown node type in type_use"
131 let uses = vec::from_mut(copy cx.uses);
132 ccx.type_use_cache.insert(fn_id, uses);
136 fn type_needs(cx: ctx, use_: uint, ty: ty::t) {
137 // Optimization -- don't descend type if all params already have this use
138 for vec::each_mut(cx.uses) |u| {
139 if *u & use_ != use_ {
140 type_needs_inner(cx, use_, ty, @Nil);
146 fn type_needs_inner(cx: ctx, use_: uint, ty: ty::t,
147 enums_seen: @List<def_id>) {
148 do ty::maybe_walk_ty(ty) |ty| {
149 if ty::type_has_params(ty) {
150 match ty::get(ty).sty {
152 This previously included ty_box -- that was wrong
153 because if we cast an @T to an trait (for example) and return
154 it, we depend on the drop glue for T (we have to write the
155 right tydesc into the result)
157 ty::ty_fn(_) | ty::ty_ptr(_) | ty::ty_rptr(_, _)
158 | ty::ty_trait(_, _, _) => false,
159 ty::ty_enum(did, substs) => {
160 if option::is_none(&list::find(enums_seen, |id| *id == did)) {
161 let seen = @Cons(did, enums_seen);
162 for vec::each(*ty::enum_variants(cx.ccx.tcx, did)) |v| {
163 for vec::each(v.args) |aty| {
164 let t = ty::subst(cx.ccx.tcx, &substs, *aty);
165 type_needs_inner(cx, use_, t, seen);
172 cx.uses[p.idx] |= use_;
181 fn node_type_needs(cx: ctx, use_: uint, id: node_id) {
182 type_needs(cx, use_, ty::node_id_to_type(cx.ccx.tcx, id));
185 fn mark_for_expr(cx: ctx, e: @expr) {
189 expr_rec(_, _) | expr_struct(*) | expr_tup(_) |
190 expr_unary(box(_), _) | expr_unary(uniq(_), _) |
191 expr_binary(add, _, _) |
192 expr_copy(_) | expr_unary_move(_) | expr_repeat(*) => {
193 node_type_needs(cx, use_repr, e.id);
195 expr_cast(base, _) => {
196 let result_t = ty::node_id_to_type(cx.ccx.tcx, e.id);
197 match ty::get(result_t).sty {
199 // When we're casting to an trait, we need the
200 // tydesc for the expr that's being cast.
201 node_type_needs(cx, use_tydesc, base.id);
206 expr_binary(op, lhs, _) => {
208 eq | lt | le | ne | ge | gt => {
209 node_type_needs(cx, use_tydesc, lhs.id)
215 do cx.ccx.tcx.node_type_substs.find(e.id).iter |ts| {
216 let id = ast_util::def_id_of_def(cx.ccx.tcx.def_map.get(e.id));
217 let uses_for_ts = type_uses_for(cx.ccx, id, ts.len());
218 for vec::each2(uses_for_ts, *ts) |uses, subst| {
219 type_needs(cx, *uses, *subst)
223 expr_fn(*) | expr_fn_block(*) => {
224 match ty::ty_fn_proto(ty::expr_ty(cx.ccx.tcx, e)) {
225 ast::ProtoBare | ast::ProtoUniq => {}
226 ast::ProtoBox | ast::ProtoBorrowed => {
227 for vec::each(*freevars::get_freevars(cx.ccx.tcx, e.id)) |fv| {
228 let node_id = ast_util::def_id_of_def(fv.def).node;
229 node_type_needs(cx, use_repr, node_id);
234 expr_assign(val, _) | expr_swap(val, _) | expr_assign_op(_, val, _) |
235 expr_ret(Some(val)) => {
236 node_type_needs(cx, use_repr, val.id);
238 expr_index(base, _) | expr_field(base, _, _) => {
239 // FIXME (#2537): could be more careful and not count fields after
241 let base_ty = ty::node_id_to_type(cx.ccx.tcx, base.id);
242 type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
244 do option::iter(&cx.ccx.maps.method_map.find(e.id)) |mth| {
246 typeck::method_static(did) => {
247 do cx.ccx.tcx.node_type_substs.find(e.id).iter |ts| {
248 let type_uses = type_uses_for(cx.ccx, did, ts.len());
249 for vec::each2(type_uses, *ts) |uses, subst| {
250 type_needs(cx, *uses, *subst)
254 typeck::method_param({param_num: param, _}) => {
255 cx.uses[param] |= use_tydesc;
257 typeck::method_trait(*) | typeck::method_self(*) => (),
261 expr_log(_, _, val) => {
262 node_type_needs(cx, use_tydesc, val.id);
264 expr_call(f, _, _) => {
266 ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, f.id))
269 expl(by_move) | expl(by_copy) | expl(by_val) => {
270 type_needs(cx, use_repr, a.ty);
276 expr_paren(e) => mark_for_expr(cx, e),
277 expr_match(*) | expr_block(_) | expr_if(*) |
278 expr_while(*) | expr_fail(_) | expr_break(_) | expr_again(_) |
279 expr_unary(_, _) | expr_lit(_) | expr_assert(_) |
280 expr_mac(_) | expr_addr_of(_, _) |
281 expr_ret(_) | expr_loop(_, _) |
282 expr_loop_body(_) | expr_do_body(_) => ()
286 fn handle_body(cx: ctx, body: blk) {
287 let v = visit::mk_vt(@{
288 visit_expr: |e, cx, v| {
289 visit::visit_expr(e, cx, v);
290 mark_for_expr(cx, e);
292 visit_local: |l, cx, v| {
293 visit::visit_local(l, cx, v);
294 node_type_needs(cx, use_repr, l.node.id);
296 visit_pat: |p, cx, v| {
297 visit::visit_pat(p, cx, v);
298 node_type_needs(cx, use_repr, p.id);
300 visit_block: |b, cx, v| {
301 visit::visit_block(b, cx, v);
302 do option::iter(&b.node.expr) |e| {
303 node_type_needs(cx, use_repr, e.id);
306 visit_item: |_i, _cx, _v| { },
307 ..*visit::default_visitor()
309 v.visit_block(body, cx, v);