auto merge of #9538 : thestinger/rust/type_use, r=pcwalton
This is broken, and results in poor performance due to the undefined behaviour in the LLVM IR. LLVM's `mergefunc` is a *much* better way of doing this since it merges based on the equality of the bytecode. For example, consider `std::repr`. It generates different code per type, but is not included in the type bounds of generics. The `mergefunc` pass works for most of our code but currently hits an assert on libstd. It is receiving attention upstream so it will be ready soon, but I don't think removing this broken code should wait any longer. I've opened #9536 about enabling it by default. Closes #8651 Closes #3547 Closes #2537 Closes #6971 Closes #9222
This commit is contained in:
commit
ae8a2ff379
@ -1,79 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
#
|
||||
# This is a tool that helps with debugging incorrect monomorphic instance collapse.
|
||||
#
|
||||
# To use:
|
||||
# $ RUST_LOG=rustc::middle::trans::monomorphize rustc ARGS 2>&1 >log.txt
|
||||
# $ ./monodebug.pl log.txt
|
||||
#
|
||||
# This will show all generics that got collapsed. You can inspect this list to find the instances
|
||||
# that were mistakenly combined into one. Fixes will (most likely) be applied to type_use.rs.
|
||||
#
|
||||
# Questions about this tool go to pcwalton.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Data::Dumper qw(Dumper);
|
||||
use Text::Balanced qw(extract_bracketed);
|
||||
|
||||
my %funcs;
|
||||
while (<>) {
|
||||
chomp;
|
||||
/^rust: ~"monomorphic_fn\((.*)"$/ or next;
|
||||
my $text = $1;
|
||||
$text =~ /fn_id=(\{ crate: \d+, node: \d+ \} \([^)]+\)), real_substs=(.*?), substs=(.*?), hash_id = \@\{ (.*) \}$/ or next;
|
||||
my ($fn_id, $real_substs, $substs, $hash_id) = ($1, $2, $3, $4);
|
||||
|
||||
#print "$hash_id\n";
|
||||
$hash_id =~ /^def: { crate: \d+, node: \d+ }, params: ~\[ (.*) \], impl_did_opt: (?:None|Some\({ crate: \d+, node: \d+ }\))$/ or next;
|
||||
my $params = $1;
|
||||
|
||||
my @real_substs;
|
||||
@real_substs = $real_substs =~ /\\"(.*?)\\"/g;
|
||||
|
||||
my @mono_params;
|
||||
while (1) {
|
||||
$params =~ s/^, //;
|
||||
if ($params =~ s/^mono_precise//) {
|
||||
extract_bracketed($params, '()');
|
||||
push @mono_params, 'precise';
|
||||
next;
|
||||
}
|
||||
if ($params =~ s/^mono_repr//) {
|
||||
my $sub = extract_bracketed($params, '()');
|
||||
push @mono_params, "repr($sub)";
|
||||
next;
|
||||
}
|
||||
if ($params =~ s/^mono_any//) {
|
||||
push @mono_params, "any";
|
||||
next;
|
||||
}
|
||||
last;
|
||||
}
|
||||
|
||||
my @key_params;
|
||||
for (my $i = 0; $i < @mono_params; ++$i) {
|
||||
if ($mono_params[$i] eq 'precise') {
|
||||
push @key_params, 'precise(' . $real_substs[$i] . ')';
|
||||
} else {
|
||||
push @key_params, $mono_params[$i];
|
||||
}
|
||||
}
|
||||
|
||||
my $key = "$fn_id with " . (join ', ', @key_params);
|
||||
$funcs{$key}{$real_substs} = 1;
|
||||
}
|
||||
|
||||
while (my ($key, $substs) = each %funcs) {
|
||||
my @params = keys %$substs;
|
||||
next if @params == 1;
|
||||
|
||||
print "$key\n";
|
||||
print(('-' x (length $key)), $/);
|
||||
for my $param (@params) {
|
||||
print "$param\n";
|
||||
}
|
||||
print "\n";
|
||||
}
|
@ -71,7 +71,6 @@ _rustc_opts_debug=(
|
||||
'count-type-sizes:count the sizes of aggregate types'
|
||||
'meta-stats:gather metadata statistics'
|
||||
'no-opt:do not optimize, even if -O is passed'
|
||||
'no-monomorphic-collapse:do not collapse template instantiations'
|
||||
'print-link-args:Print the arguments passed to the linker'
|
||||
'gc:Garbage collect shared data (experimental)'
|
||||
'jit:Execute using JIT (experimental)'
|
||||
|
@ -748,13 +748,6 @@ pub fn build_session_options(binary: @str,
|
||||
let debuginfo = debugging_opts & session::debug_info != 0 ||
|
||||
extra_debuginfo;
|
||||
|
||||
// If debugging info is generated, do not collapse monomorphized function instances.
|
||||
// Functions with equivalent llvm code still need separate debugging descriptions because names
|
||||
// might differ.
|
||||
if debuginfo {
|
||||
debugging_opts |= session::no_monomorphic_collapse;
|
||||
}
|
||||
|
||||
let statik = debugging_opts & session::statik != 0;
|
||||
|
||||
let addl_lib_search_paths = matches.opt_strs("L").map(|s| Path(*s));
|
||||
|
@ -67,20 +67,19 @@ pub static debug_llvm: uint = 1 << 13;
|
||||
pub static count_type_sizes: uint = 1 << 14;
|
||||
pub static meta_stats: uint = 1 << 15;
|
||||
pub static no_opt: uint = 1 << 16;
|
||||
pub static no_monomorphic_collapse: uint = 1 << 17;
|
||||
pub static gc: uint = 1 << 18;
|
||||
pub static jit: uint = 1 << 19;
|
||||
pub static debug_info: uint = 1 << 20;
|
||||
pub static extra_debug_info: uint = 1 << 21;
|
||||
pub static statik: uint = 1 << 22;
|
||||
pub static print_link_args: uint = 1 << 23;
|
||||
pub static no_debug_borrows: uint = 1 << 24;
|
||||
pub static lint_llvm: uint = 1 << 25;
|
||||
pub static once_fns: uint = 1 << 26;
|
||||
pub static print_llvm_passes: uint = 1 << 27;
|
||||
pub static no_vectorize_loops: uint = 1 << 28;
|
||||
pub static no_vectorize_slp: uint = 1 << 29;
|
||||
pub static no_prepopulate_passes: uint = 1 << 30;
|
||||
pub static gc: uint = 1 << 17;
|
||||
pub static jit: uint = 1 << 18;
|
||||
pub static debug_info: uint = 1 << 19;
|
||||
pub static extra_debug_info: uint = 1 << 20;
|
||||
pub static statik: uint = 1 << 21;
|
||||
pub static print_link_args: uint = 1 << 22;
|
||||
pub static no_debug_borrows: uint = 1 << 23;
|
||||
pub static lint_llvm: uint = 1 << 24;
|
||||
pub static once_fns: uint = 1 << 25;
|
||||
pub static print_llvm_passes: uint = 1 << 26;
|
||||
pub static no_vectorize_loops: uint = 1 << 27;
|
||||
pub static no_vectorize_slp: uint = 1 << 28;
|
||||
pub static no_prepopulate_passes: uint = 1 << 29;
|
||||
|
||||
pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
|
||||
~[(~"verbose", ~"in general, enable more debug printouts", verbose),
|
||||
@ -106,8 +105,6 @@ pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
|
||||
count_type_sizes),
|
||||
(~"meta-stats", ~"gather metadata statistics", meta_stats),
|
||||
(~"no-opt", ~"do not optimize, even if -O is passed", no_opt),
|
||||
(~"no-monomorphic-collapse", ~"do not collapse template instantiations",
|
||||
no_monomorphic_collapse),
|
||||
(~"print-link-args", ~"Print the arguments passed to the linker", print_link_args),
|
||||
(~"gc", ~"Garbage collect shared data (experimental)", gc),
|
||||
(~"jit", ~"Execute using JIT (experimental)", jit),
|
||||
@ -326,9 +323,6 @@ impl Session_ {
|
||||
pub fn borrowck_note_loan(&self) -> bool {
|
||||
self.debugging_opt(borrowck_note_loan)
|
||||
}
|
||||
pub fn no_monomorphic_collapse(&self) -> bool {
|
||||
self.debugging_opt(no_monomorphic_collapse)
|
||||
}
|
||||
pub fn debug_borrows(&self) -> bool {
|
||||
self.opts.optimize == No && !self.debugging_opt(no_debug_borrows)
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ use middle::trans::adt;
|
||||
use middle::trans::base;
|
||||
use middle::trans::builder::Builder;
|
||||
use middle::trans::debuginfo;
|
||||
use middle::trans::type_use;
|
||||
use middle::trans::common::{C_i32, C_null};
|
||||
use middle::ty;
|
||||
|
||||
@ -73,8 +72,6 @@ pub struct CrateContext {
|
||||
// Cache instances of monomorphized functions
|
||||
monomorphized: HashMap<mono_id, ValueRef>,
|
||||
monomorphizing: HashMap<ast::DefId, uint>,
|
||||
// Cache computed type parameter uses (see type_use.rs)
|
||||
type_use_cache: HashMap<ast::DefId, @~[type_use::type_uses]>,
|
||||
// Cache generated vtables
|
||||
vtables: HashMap<(ty::t, mono_id), ValueRef>,
|
||||
// Cache of constant strings,
|
||||
@ -204,7 +201,6 @@ impl CrateContext {
|
||||
non_inlineable_statics: HashSet::new(),
|
||||
monomorphized: HashMap::new(),
|
||||
monomorphizing: HashMap::new(),
|
||||
type_use_cache: HashMap::new(),
|
||||
vtables: HashMap::new(),
|
||||
const_cstr_cache: HashMap::new(),
|
||||
const_globals: HashMap::new(),
|
||||
|
@ -520,8 +520,7 @@ pub fn vtable_id(ccx: @mut CrateContext,
|
||||
monomorphize::make_mono_id(
|
||||
ccx,
|
||||
impl_id,
|
||||
&psubsts,
|
||||
None)
|
||||
&psubsts)
|
||||
}
|
||||
|
||||
// can't this be checked at the callee?
|
||||
|
@ -38,7 +38,6 @@ pub mod foreign;
|
||||
pub mod intrinsic;
|
||||
pub mod reflect;
|
||||
pub mod debuginfo;
|
||||
pub mod type_use;
|
||||
pub mod machine;
|
||||
pub mod adt;
|
||||
pub mod asm;
|
||||
|
@ -18,15 +18,11 @@ use middle::trans::base::{trans_fn, decl_internal_rust_fn};
|
||||
use middle::trans::base::{get_item_val, no_self};
|
||||
use middle::trans::base;
|
||||
use middle::trans::common::*;
|
||||
use middle::trans::datum;
|
||||
use middle::trans::machine;
|
||||
use middle::trans::meth;
|
||||
use middle::trans::type_of;
|
||||
use middle::trans::type_use;
|
||||
use middle::trans::intrinsic;
|
||||
use middle::ty;
|
||||
use middle::typeck;
|
||||
use util::ppaux::{Repr,ty_to_str};
|
||||
use util::ppaux::Repr;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::ast_map;
|
||||
@ -65,10 +61,8 @@ pub fn monomorphic_fn(ccx: @mut CrateContext,
|
||||
|
||||
for s in real_substs.tps.iter() { assert!(!ty::type_has_params(*s)); }
|
||||
for s in psubsts.tys.iter() { assert!(!ty::type_has_params(*s)); }
|
||||
let param_uses = type_use::type_uses_for(ccx, fn_id, psubsts.tys.len());
|
||||
|
||||
|
||||
let hash_id = make_mono_id(ccx, fn_id, &*psubsts, Some(param_uses));
|
||||
let hash_id = make_mono_id(ccx, fn_id, &*psubsts);
|
||||
if hash_id.params.iter().any(
|
||||
|p| match *p { mono_precise(_, _) => false, _ => true }) {
|
||||
must_cast = true;
|
||||
@ -302,8 +296,7 @@ pub fn monomorphic_fn(ccx: @mut CrateContext,
|
||||
|
||||
pub fn make_mono_id(ccx: @mut CrateContext,
|
||||
item: ast::DefId,
|
||||
substs: ¶m_substs,
|
||||
param_uses: Option<@~[type_use::type_uses]>) -> mono_id {
|
||||
substs: ¶m_substs) -> mono_id {
|
||||
// FIXME (possibly #5801): Need a lot of type hints to get
|
||||
// .collect() to work.
|
||||
let substs_iter = substs.self_ty.iter().chain(substs.tys.iter());
|
||||
@ -321,59 +314,9 @@ pub fn make_mono_id(ccx: @mut CrateContext,
|
||||
};
|
||||
|
||||
|
||||
let param_ids = match param_uses {
|
||||
Some(ref uses) => {
|
||||
// param_uses doesn't include a use for the self type.
|
||||
// We just say it is fully used.
|
||||
let self_use =
|
||||
substs.self_ty.map(|_| type_use::use_repr|type_use::use_tydesc);
|
||||
let uses_iter = self_use.iter().chain(uses.iter());
|
||||
|
||||
precise_param_ids.iter().zip(uses_iter).map(|(id, uses)| {
|
||||
if ccx.sess.no_monomorphic_collapse() {
|
||||
match *id {
|
||||
(a, b) => mono_precise(a, b)
|
||||
}
|
||||
} else {
|
||||
match *id {
|
||||
(a, b@Some(_)) => mono_precise(a, b),
|
||||
(subst, None) => {
|
||||
if *uses == 0 {
|
||||
mono_any
|
||||
} else if *uses == type_use::use_repr &&
|
||||
!ty::type_needs_drop(ccx.tcx, subst)
|
||||
{
|
||||
let llty = type_of::type_of(ccx, subst);
|
||||
let size = machine::llbitsize_of_real(ccx, llty);
|
||||
let align = machine::llalign_of_min(ccx, llty);
|
||||
let mode = datum::appropriate_mode(ccx.tcx, subst);
|
||||
let data_class = mono_data_classify(subst);
|
||||
|
||||
debug!("make_mono_id: type %s -> size %u align %u mode %? class %?",
|
||||
ty_to_str(ccx.tcx, subst),
|
||||
size, align, mode, data_class);
|
||||
|
||||
// Special value for nil to prevent problems
|
||||
// with undef return pointers.
|
||||
if size <= 8u && ty::type_is_nil(subst) {
|
||||
mono_repr(0u, 0u, data_class, mode)
|
||||
} else {
|
||||
mono_repr(size, align, data_class, mode)
|
||||
}
|
||||
} else {
|
||||
mono_precise(subst, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
None => {
|
||||
precise_param_ids.iter().map(|x| {
|
||||
let (a, b) = *x;
|
||||
mono_precise(a, b)
|
||||
}).collect()
|
||||
}
|
||||
};
|
||||
let param_ids = precise_param_ids.iter().map(|x| {
|
||||
let (a, b) = *x;
|
||||
mono_precise(a, b)
|
||||
}).collect();
|
||||
@mono_id_ {def: item, params: param_ids}
|
||||
}
|
||||
|
@ -1,449 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// Determines the ways in which a generic function body depends
|
||||
// on its type parameters. Used to aggressively reuse compiled
|
||||
// function bodies for different types.
|
||||
|
||||
// This unfortunately depends on quite a bit of knowledge about the
|
||||
// details of the language semantics, and is likely to accidentally go
|
||||
// out of sync when something is changed. It could be made more
|
||||
// powerful by distinguishing between functions that only need to know
|
||||
// the size and alignment of a type, and those that also use its
|
||||
// drop/take glue. But this would increase the fragility of the code
|
||||
// to a ridiculous level, and probably not catch all that many extra
|
||||
// opportunities for reuse.
|
||||
|
||||
// (An other approach to doing what this code does is to instrument
|
||||
// the translation code to set flags whenever it does something like
|
||||
// alloca a type or get a tydesc. This would not duplicate quite as
|
||||
// much information, but have the disadvantage of being very
|
||||
// invasive.)
|
||||
|
||||
use middle::freevars;
|
||||
use middle::trans::common::*;
|
||||
use middle::trans::inline;
|
||||
use middle::ty;
|
||||
use middle::typeck;
|
||||
|
||||
use std::option::{Some, None};
|
||||
use std::vec;
|
||||
use extra::list::{List, Cons, Nil};
|
||||
use extra::list;
|
||||
use syntax::ast;
|
||||
use syntax::ast::*;
|
||||
use syntax::ast_map;
|
||||
use syntax::ast_util;
|
||||
use syntax::parse::token;
|
||||
use syntax::visit;
|
||||
use syntax::visit::Visitor;
|
||||
|
||||
pub type type_uses = uint; // Bitmask
|
||||
pub static use_repr: uint = 1; /* Dependency on size/alignment/mode and
|
||||
take/drop glue */
|
||||
pub static use_tydesc: uint = 2; /* Takes the tydesc, or compares */
|
||||
pub static use_all: uint = use_repr|use_tydesc;
|
||||
|
||||
#[deriving(Clone)]
|
||||
pub struct Context {
|
||||
ccx: @mut CrateContext,
|
||||
uses: @mut ~[type_uses]
|
||||
}
|
||||
|
||||
pub fn type_uses_for(ccx: @mut CrateContext, fn_id: DefId, n_tps: uint)
|
||||
-> @~[type_uses] {
|
||||
|
||||
fn store_type_uses(cx: Context, fn_id: DefId) -> @~[type_uses] {
|
||||
let Context { uses, ccx } = cx;
|
||||
let uses = @(*uses).clone(); // freeze
|
||||
ccx.type_use_cache.insert(fn_id, uses);
|
||||
uses
|
||||
}
|
||||
|
||||
match ccx.type_use_cache.find(&fn_id) {
|
||||
Some(uses) => return *uses,
|
||||
None => ()
|
||||
}
|
||||
|
||||
let fn_id_loc = if fn_id.crate == LOCAL_CRATE {
|
||||
fn_id
|
||||
} else {
|
||||
inline::maybe_instantiate_inline(ccx, fn_id)
|
||||
};
|
||||
|
||||
// Conservatively assume full use for recursive loops
|
||||
ccx.type_use_cache.insert(fn_id, @vec::from_elem(n_tps, use_all));
|
||||
|
||||
let mut cx = Context {
|
||||
ccx: ccx,
|
||||
uses: @mut vec::from_elem(n_tps, 0u)
|
||||
};
|
||||
|
||||
// If the method is a default method, we mark all of the types as
|
||||
// used. This is imprecise, but simple. Getting it right is
|
||||
// tricky because the substs on the call and the substs on the
|
||||
// default method differ, because of substs on the trait/impl.
|
||||
let is_default = ty::provided_source(ccx.tcx, fn_id_loc).is_some();
|
||||
// We also mark all of the params as used if it is an extern thing
|
||||
// that we haven't been able to inline yet.
|
||||
if is_default || fn_id_loc.crate != LOCAL_CRATE {
|
||||
for n in range(0u, n_tps) { cx.uses[n] |= use_all; }
|
||||
return store_type_uses(cx, fn_id);
|
||||
}
|
||||
|
||||
let map_node = match ccx.tcx.items.find(&fn_id_loc.node) {
|
||||
Some(x) => {
|
||||
(*x).clone()
|
||||
}
|
||||
None => {
|
||||
ccx.sess.bug(fmt!("type_uses_for: unbound item ID %?",
|
||||
fn_id_loc))
|
||||
}
|
||||
};
|
||||
|
||||
match map_node {
|
||||
ast_map::node_item(@ast::item { node: item_fn(_, _, _, _, ref body),
|
||||
_ }, _) |
|
||||
ast_map::node_method(@ast::method {body: ref body, _}, _, _) => {
|
||||
handle_body(&mut cx, body);
|
||||
}
|
||||
ast_map::node_trait_method(*) => {
|
||||
// This will be a static trait method. For now, we just assume
|
||||
// it fully depends on all of the type information. (Doing
|
||||
// otherwise would require finding the actual implementation).
|
||||
for n in range(0u, n_tps) { cx.uses[n] |= use_all;}
|
||||
// We need to return early, before the arguments are processed,
|
||||
// because of difficulties in the handling of Self.
|
||||
return store_type_uses(cx, fn_id);
|
||||
}
|
||||
ast_map::node_variant(_, _, _) => {
|
||||
for n in range(0u, n_tps) { cx.uses[n] |= use_repr;}
|
||||
}
|
||||
ast_map::node_foreign_item(i@@foreign_item {
|
||||
node: foreign_item_fn(*),
|
||||
_
|
||||
},
|
||||
abi,
|
||||
_,
|
||||
_) => {
|
||||
if abi.is_intrinsic() {
|
||||
let nm = cx.ccx.sess.str_of(i.ident);
|
||||
let name = nm.as_slice();
|
||||
let flags = if name.starts_with("atomic_") {
|
||||
0
|
||||
} else {
|
||||
match name {
|
||||
"size_of" | "pref_align_of" | "min_align_of" |
|
||||
"uninit" | "init" | "transmute" | "move_val" |
|
||||
"move_val_init" => use_repr,
|
||||
|
||||
"get_tydesc" | "needs_drop" | "contains_managed" => use_tydesc,
|
||||
|
||||
"visit_tydesc" | "forget" | "frame_address" |
|
||||
"morestack_addr" => 0,
|
||||
|
||||
"offset" |
|
||||
"memcpy32" | "memcpy64" | "memmove32" | "memmove64" |
|
||||
"memset32" | "memset64" => use_repr,
|
||||
|
||||
"sqrtf32" | "sqrtf64" | "powif32" | "powif64" |
|
||||
"sinf32" | "sinf64" | "cosf32" | "cosf64" |
|
||||
"powf32" | "powf64" | "expf32" | "expf64" |
|
||||
"exp2f32" | "exp2f64" | "logf32" | "logf64" |
|
||||
"log10f32"| "log10f64"| "log2f32" | "log2f64" |
|
||||
"fmaf32" | "fmaf64" | "fabsf32" | "fabsf64" |
|
||||
"floorf32"| "floorf64"| "ceilf32" | "ceilf64" |
|
||||
"truncf32"| "truncf64" => 0,
|
||||
|
||||
"ctpop8" | "ctpop16" | "ctpop32" | "ctpop64" => 0,
|
||||
|
||||
"ctlz8" | "ctlz16" | "ctlz32" | "ctlz64" => 0,
|
||||
"cttz8" | "cttz16" | "cttz32" | "cttz64" => 0,
|
||||
|
||||
"bswap16" | "bswap32" | "bswap64" => 0,
|
||||
|
||||
|
||||
"i8_add_with_overflow" | "u8_add_with_overflow" |
|
||||
"i16_add_with_overflow" | "u16_add_with_overflow" |
|
||||
"i32_add_with_overflow" | "u32_add_with_overflow" |
|
||||
"i64_add_with_overflow" | "u64_add_with_overflow" => 0,
|
||||
|
||||
"i8_sub_with_overflow" | "u8_sub_with_overflow" |
|
||||
"i16_sub_with_overflow" | "u16_sub_with_overflow" |
|
||||
"i32_sub_with_overflow" | "u32_sub_with_overflow" |
|
||||
"i64_sub_with_overflow" | "u64_sub_with_overflow" => 0,
|
||||
|
||||
"i8_mul_with_overflow" | "u8_mul_with_overflow" |
|
||||
"i16_mul_with_overflow" | "u16_mul_with_overflow" |
|
||||
"i32_mul_with_overflow" | "u32_mul_with_overflow" |
|
||||
"i64_mul_with_overflow" | "u64_mul_with_overflow" => 0,
|
||||
|
||||
// would be cool to make these an enum instead of
|
||||
// strings!
|
||||
_ => fail!("unknown intrinsic in type_use")
|
||||
}
|
||||
};
|
||||
for n in range(0u, n_tps) { cx.uses[n] |= flags;}
|
||||
}
|
||||
}
|
||||
ast_map::node_struct_ctor(*) => {
|
||||
// Similarly to node_variant, this monomorphized function just
|
||||
// uses the representations of all of its type parameters.
|
||||
for n in range(0u, n_tps) { cx.uses[n] |= use_repr; }
|
||||
}
|
||||
_ => {
|
||||
ccx.tcx.sess.bug(fmt!("unknown node type in type_use: %s",
|
||||
ast_map::node_id_to_str(
|
||||
ccx.tcx.items,
|
||||
fn_id_loc.node,
|
||||
token::get_ident_interner())));
|
||||
}
|
||||
}
|
||||
|
||||
// Now handle arguments
|
||||
match ty::get(ty::lookup_item_type(cx.ccx.tcx, fn_id).ty).sty {
|
||||
ty::ty_bare_fn(ty::BareFnTy {sig: ref sig, _}) |
|
||||
ty::ty_closure(ty::ClosureTy {sig: ref sig, _}) => {
|
||||
for arg in sig.inputs.iter() {
|
||||
type_needs(&cx, use_repr, *arg);
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
|
||||
store_type_uses(cx, fn_id)
|
||||
}
|
||||
|
||||
pub fn type_needs(cx: &Context, use_: uint, ty: ty::t) {
|
||||
// Optimization -- don't descend type if all params already have this use
|
||||
if cx.uses.iter().any(|&elt| elt & use_ != use_) {
|
||||
type_needs_inner(cx, use_, ty, @Nil);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_needs_inner(cx: &Context,
|
||||
use_: uint,
|
||||
ty: ty::t,
|
||||
enums_seen: @List<DefId>) {
|
||||
do ty::maybe_walk_ty(ty) |ty| {
|
||||
if ty::type_has_params(ty) {
|
||||
match ty::get(ty).sty {
|
||||
/*
|
||||
This previously included ty_box -- that was wrong
|
||||
because if we cast an @T to an trait (for example) and return
|
||||
it, we depend on the drop glue for T (we have to write the
|
||||
right tydesc into the result)
|
||||
*/
|
||||
ty::ty_closure(*) |
|
||||
ty::ty_bare_fn(*) |
|
||||
ty::ty_ptr(_) |
|
||||
ty::ty_rptr(_, _) |
|
||||
ty::ty_trait(*) => false,
|
||||
|
||||
ty::ty_enum(did, ref substs) => {
|
||||
if list::find(enums_seen, |id| *id == did).is_none() {
|
||||
let seen = @Cons(did, enums_seen);
|
||||
let r = ty::enum_variants(cx.ccx.tcx, did);
|
||||
for v in r.iter() {
|
||||
for aty in v.args.iter() {
|
||||
let t = ty::subst(cx.ccx.tcx, &(*substs), *aty);
|
||||
type_needs_inner(cx, use_, t, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
ty::ty_param(p) => {
|
||||
cx.uses[p.idx] |= use_;
|
||||
false
|
||||
}
|
||||
_ => true
|
||||
}
|
||||
} else { false }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_type_needs(cx: &Context, use_: uint, id: NodeId) {
|
||||
type_needs(cx, use_, ty::node_id_to_type(cx.ccx.tcx, id));
|
||||
}
|
||||
|
||||
pub fn mark_for_method_call(cx: &Context, e_id: NodeId, callee_id: NodeId) {
|
||||
let mut opt_static_did = None;
|
||||
{
|
||||
let r = cx.ccx.maps.method_map.find(&e_id);
|
||||
for mth in r.iter() {
|
||||
match mth.origin {
|
||||
typeck::method_static(did) => {
|
||||
opt_static_did = Some(did);
|
||||
}
|
||||
typeck::method_param(typeck::method_param {
|
||||
param_num: typeck::param_numbered(param),
|
||||
_
|
||||
}) => {
|
||||
cx.uses[param] |= use_tydesc;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: we do not execute this code from within the each() call
|
||||
// above because the recursive call to `type_needs` can trigger
|
||||
// inlining and hence can cause `method_map` and
|
||||
// `node_type_substs` to be modified.
|
||||
for &did in opt_static_did.iter() {
|
||||
{
|
||||
let r = cx.ccx.tcx.node_type_substs.find_copy(&callee_id);
|
||||
for ts in r.iter() {
|
||||
let type_uses = type_uses_for(cx.ccx, did, ts.len());
|
||||
for (uses, subst) in type_uses.iter().zip(ts.iter()) {
|
||||
type_needs(cx, *uses, *subst)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mark_for_expr(cx: &Context, e: &Expr) {
|
||||
match e.node {
|
||||
ExprVstore(_, _) | ExprVec(_, _) | ExprStruct(*) | ExprTup(_) |
|
||||
ExprUnary(_, UnBox(_), _) | ExprUnary(_, UnUniq, _) |
|
||||
ExprBinary(_, BiAdd, _, _) | ExprRepeat(*) => {
|
||||
node_type_needs(cx, use_repr, e.id);
|
||||
}
|
||||
ExprCast(base, _) => {
|
||||
let result_t = ty::node_id_to_type(cx.ccx.tcx, e.id);
|
||||
match ty::get(result_t).sty {
|
||||
ty::ty_trait(*) => {
|
||||
// When we're casting to an trait, we need the
|
||||
// tydesc for the expr that's being cast.
|
||||
node_type_needs(cx, use_tydesc, base.id);
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
ExprBinary(_, op, lhs, _) => {
|
||||
match op {
|
||||
BiEq | BiLt | BiLe | BiNe | BiGe | BiGt => {
|
||||
node_type_needs(cx, use_tydesc, lhs.id)
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
ExprPath(_) | ExprSelf => {
|
||||
let opt_ts = cx.ccx.tcx.node_type_substs.find_copy(&e.id);
|
||||
for ts in opt_ts.iter() {
|
||||
let id = ast_util::def_id_of_def(cx.ccx.tcx.def_map.get_copy(&e.id));
|
||||
let uses_for_ts = type_uses_for(cx.ccx, id, ts.len());
|
||||
for (uses, subst) in uses_for_ts.iter().zip(ts.iter()) {
|
||||
type_needs(cx, *uses, *subst)
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprFnBlock(*) => {
|
||||
match ty::ty_closure_sigil(ty::expr_ty(cx.ccx.tcx, e)) {
|
||||
ast::OwnedSigil => {}
|
||||
ast::BorrowedSigil | ast::ManagedSigil => {
|
||||
for fv in freevars::get_freevars(cx.ccx.tcx, e.id).iter() {
|
||||
let node_id = ast_util::def_id_of_def(fv.def).node;
|
||||
node_type_needs(cx, use_repr, node_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprAssign(val, _) | ExprAssignOp(_, _, val, _) |
|
||||
ExprRet(Some(val)) => {
|
||||
node_type_needs(cx, use_repr, val.id);
|
||||
}
|
||||
ExprIndex(callee_id, base, _) => {
|
||||
// FIXME (#2537): could be more careful and not count fields after
|
||||
// the chosen field.
|
||||
let base_ty = ty::node_id_to_type(cx.ccx.tcx, base.id);
|
||||
type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
|
||||
mark_for_method_call(cx, e.id, callee_id);
|
||||
}
|
||||
ExprField(base, _, _) => {
|
||||
// Method calls are now a special syntactic form,
|
||||
// so `a.b` should always be a field.
|
||||
assert!(!cx.ccx.maps.method_map.contains_key(&e.id));
|
||||
|
||||
let base_ty = ty::node_id_to_type(cx.ccx.tcx, base.id);
|
||||
type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
|
||||
}
|
||||
ExprCall(f, _, _) => {
|
||||
let r = ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, f.id));
|
||||
for a in r.iter() {
|
||||
type_needs(cx, use_repr, *a);
|
||||
}
|
||||
}
|
||||
ExprMethodCall(callee_id, rcvr, _, _, _, _) => {
|
||||
let base_ty = ty::node_id_to_type(cx.ccx.tcx, rcvr.id);
|
||||
type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
|
||||
|
||||
let r = ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, callee_id));
|
||||
for a in r.iter() {
|
||||
type_needs(cx, use_repr, *a);
|
||||
}
|
||||
mark_for_method_call(cx, e.id, callee_id);
|
||||
}
|
||||
|
||||
ExprInlineAsm(ref ia) => {
|
||||
for &(_, input) in ia.inputs.iter() {
|
||||
node_type_needs(cx, use_repr, input.id);
|
||||
}
|
||||
for &(_, out) in ia.outputs.iter() {
|
||||
node_type_needs(cx, use_repr, out.id);
|
||||
}
|
||||
}
|
||||
|
||||
ExprParen(e) => mark_for_expr(cx, e),
|
||||
|
||||
ExprMatch(*) | ExprBlock(_) | ExprIf(*) | ExprWhile(*) |
|
||||
ExprBreak(_) | ExprAgain(_) | ExprUnary(*) | ExprLit(_) |
|
||||
ExprMac(_) | ExprAddrOf(*) | ExprRet(_) | ExprLoop(*) |
|
||||
ExprDoBody(_) | ExprLogLevel => (),
|
||||
|
||||
ExprForLoop(*) => fail!("non-desugared expr_for_loop")
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor<()> for Context {
|
||||
|
||||
fn visit_expr(&mut self, e:@Expr, _: ()) {
|
||||
visit::walk_expr(self, e, ());
|
||||
mark_for_expr(self, e);
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l:@Local, _:()) {
|
||||
visit::walk_local(self, l, ());
|
||||
node_type_needs(self, use_repr, l.id);
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, p:@Pat, _: ()) {
|
||||
visit::walk_pat(self, p, ());
|
||||
node_type_needs(self, use_repr, p.id);
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, b:&Block, _: ()) {
|
||||
visit::walk_block(self, b, ());
|
||||
for e in b.expr.iter() {
|
||||
node_type_needs(self, use_repr, e.id);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, _:@item, _: ()) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn handle_body(cx: &mut Context, body: &Block) {
|
||||
cx.visit_block(body, ());
|
||||
}
|
Loading…
Reference in New Issue
Block a user