Cache more results of const_eval
According to http://huonw.github.io/isrustfastyet/mem/#012f909, the "const marking" pass generates about 400MB of extra memory during compilation. It appears that this is due to two different factors: 1. There is a `ccache` map in the ty::ctxt which is only ever used in this pass, so this commit moves the map out of the ty::ctxt struct and into just this pass's visitor. This turned out to not benefit that much in memory (as indicated by http://i.imgur.com/Eo4iOzK.png), but it's helpful to do nonetheless. 2. During const_eval, there are a lot of lookups into decoding inlined items from external crates. There is no caching involved here, so the same static or variant could be re-translated many times. After adding separate caches for variants and statics, the memory peak of compiling rustc decreased by 200MB (as evident by http://i.imgur.com/ULAUMtq.png) The culmination of this is basically a slight reorganization of a caching map for the const_eval pass along with a 200MB decrease in peak memory usage when compiling librustc.
This commit is contained in:
parent
8cb3426f16
commit
1996a11bb2
@ -60,6 +60,8 @@ pub enum constness {
|
|||||||
non_const
|
non_const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type constness_cache = HashMap<ast::DefId, constness>;
|
||||||
|
|
||||||
pub fn join(a: constness, b: constness) -> constness {
|
pub fn join(a: constness, b: constness) -> constness {
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(integral_const, integral_const) => integral_const,
|
(integral_const, integral_const) => integral_const,
|
||||||
@ -74,102 +76,12 @@ pub fn join_all<It: Iterator<constness>>(mut cs: It) -> constness {
|
|||||||
cs.fold(integral_const, |a, b| join(a, b))
|
cs.fold(integral_const, |a, b| join(a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn classify(e: &Expr,
|
|
||||||
tcx: ty::ctxt)
|
|
||||||
-> constness {
|
|
||||||
let did = ast_util::local_def(e.id);
|
|
||||||
match tcx.ccache.find(&did) {
|
|
||||||
Some(&x) => x,
|
|
||||||
None => {
|
|
||||||
let cn =
|
|
||||||
match e.node {
|
|
||||||
ast::ExprLit(lit) => {
|
|
||||||
match lit.node {
|
|
||||||
ast::lit_str(*) |
|
|
||||||
ast::lit_float(*) => general_const,
|
|
||||||
_ => integral_const
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprUnary(_, _, inner) |
|
|
||||||
ast::ExprParen(inner) => {
|
|
||||||
classify(inner, tcx)
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprBinary(_, _, a, b) => {
|
|
||||||
join(classify(a, tcx),
|
|
||||||
classify(b, tcx))
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprTup(ref es) |
|
|
||||||
ast::ExprVec(ref es, ast::MutImmutable) => {
|
|
||||||
join_all(es.iter().map(|e| classify(*e, tcx)))
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprVstore(e, vstore) => {
|
|
||||||
match vstore {
|
|
||||||
ast::ExprVstoreSlice => classify(e, tcx),
|
|
||||||
ast::ExprVstoreUniq |
|
|
||||||
ast::ExprVstoreBox |
|
|
||||||
ast::ExprVstoreMutBox |
|
|
||||||
ast::ExprVstoreMutSlice => non_const
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprStruct(_, ref fs, None) => {
|
|
||||||
let cs = do fs.iter().map |f| {
|
|
||||||
classify(f.expr, tcx)
|
|
||||||
};
|
|
||||||
join_all(cs)
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprCast(base, _) => {
|
|
||||||
let ty = ty::expr_ty(tcx, e);
|
|
||||||
let base = classify(base, tcx);
|
|
||||||
if ty::type_is_integral(ty) {
|
|
||||||
join(integral_const, base)
|
|
||||||
} else if ty::type_is_fp(ty) {
|
|
||||||
join(general_const, base)
|
|
||||||
} else {
|
|
||||||
non_const
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprField(base, _, _) => {
|
|
||||||
classify(base, tcx)
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprIndex(_, base, idx) => {
|
|
||||||
join(classify(base, tcx),
|
|
||||||
classify(idx, tcx))
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprAddrOf(ast::MutImmutable, base) => {
|
|
||||||
classify(base, tcx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: (#3728) we can probably do something CCI-ish
|
|
||||||
// surrounding nonlocal constants. But we don't yet.
|
|
||||||
ast::ExprPath(_) => {
|
|
||||||
lookup_constness(tcx, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::ExprRepeat(*) => general_const,
|
|
||||||
|
|
||||||
_ => non_const
|
|
||||||
};
|
|
||||||
tcx.ccache.insert(did, cn);
|
|
||||||
cn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup_const(tcx: ty::ctxt, e: &Expr) -> Option<@Expr> {
|
pub fn lookup_const(tcx: ty::ctxt, e: &Expr) -> Option<@Expr> {
|
||||||
match tcx.def_map.find(&e.id) {
|
match tcx.def_map.find(&e.id) {
|
||||||
Some(&ast::DefStatic(def_id, false)) => lookup_const_by_id(tcx, def_id),
|
Some(&ast::DefStatic(def_id, false)) =>
|
||||||
Some(&ast::DefVariant(enum_def, variant_def, _)) => lookup_variant_by_id(tcx,
|
lookup_const_by_id(tcx, def_id),
|
||||||
enum_def,
|
Some(&ast::DefVariant(enum_def, variant_def, _)) =>
|
||||||
variant_def),
|
lookup_variant_by_id(tcx, enum_def, variant_def),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,6 +111,10 @@ pub fn lookup_variant_by_id(tcx: ty::ctxt,
|
|||||||
Some(_) => None
|
Some(_) => None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
match tcx.extern_const_variants.find(&variant_def) {
|
||||||
|
Some(&e) => return e,
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
let maps = astencode::Maps {
|
let maps = astencode::Maps {
|
||||||
root_map: @mut HashMap::new(),
|
root_map: @mut HashMap::new(),
|
||||||
method_map: @mut HashMap::new(),
|
method_map: @mut HashMap::new(),
|
||||||
@ -206,7 +122,7 @@ pub fn lookup_variant_by_id(tcx: ty::ctxt,
|
|||||||
write_guard_map: @mut HashSet::new(),
|
write_guard_map: @mut HashSet::new(),
|
||||||
capture_map: @mut HashMap::new()
|
capture_map: @mut HashMap::new()
|
||||||
};
|
};
|
||||||
match csearch::maybe_get_item_ast(tcx, enum_def,
|
let e = match csearch::maybe_get_item_ast(tcx, enum_def,
|
||||||
|a, b, c, d| astencode::decode_inlined_item(a,
|
|a, b, c, d| astencode::decode_inlined_item(a,
|
||||||
b,
|
b,
|
||||||
maps,
|
maps,
|
||||||
@ -219,7 +135,9 @@ pub fn lookup_variant_by_id(tcx: ty::ctxt,
|
|||||||
_ => None
|
_ => None
|
||||||
},
|
},
|
||||||
_ => None
|
_ => None
|
||||||
}
|
};
|
||||||
|
tcx.extern_const_variants.insert(variant_def, e);
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,6 +154,10 @@ pub fn lookup_const_by_id(tcx: ty::ctxt,
|
|||||||
Some(_) => None
|
Some(_) => None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
match tcx.extern_const_statics.find(&def_id) {
|
||||||
|
Some(&e) => return e,
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
let maps = astencode::Maps {
|
let maps = astencode::Maps {
|
||||||
root_map: @mut HashMap::new(),
|
root_map: @mut HashMap::new(),
|
||||||
method_map: @mut HashMap::new(),
|
method_map: @mut HashMap::new(),
|
||||||
@ -243,42 +165,125 @@ pub fn lookup_const_by_id(tcx: ty::ctxt,
|
|||||||
write_guard_map: @mut HashSet::new(),
|
write_guard_map: @mut HashSet::new(),
|
||||||
capture_map: @mut HashMap::new()
|
capture_map: @mut HashMap::new()
|
||||||
};
|
};
|
||||||
match csearch::maybe_get_item_ast(tcx, def_id,
|
let e = match csearch::maybe_get_item_ast(tcx, def_id,
|
||||||
|a, b, c, d| astencode::decode_inlined_item(a, b, maps, c, d)) {
|
|a, b, c, d| astencode::decode_inlined_item(a, b, maps, c, d)) {
|
||||||
csearch::found(ast::ii_item(item)) => match item.node {
|
csearch::found(ast::ii_item(item)) => match item.node {
|
||||||
item_static(_, ast::MutImmutable, const_expr) => Some(const_expr),
|
item_static(_, ast::MutImmutable, const_expr) => Some(const_expr),
|
||||||
_ => None
|
_ => None
|
||||||
},
|
},
|
||||||
_ => None
|
_ => None
|
||||||
}
|
};
|
||||||
|
tcx.extern_const_statics.insert(def_id, e);
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_constness(tcx: ty::ctxt, e: &Expr) -> constness {
|
struct ConstEvalVisitor {
|
||||||
match lookup_const(tcx, e) {
|
tcx: ty::ctxt,
|
||||||
Some(rhs) => {
|
ccache: constness_cache,
|
||||||
let ty = ty::expr_ty(tcx, rhs);
|
}
|
||||||
if ty::type_is_integral(ty) {
|
|
||||||
integral_const
|
impl ConstEvalVisitor {
|
||||||
} else {
|
fn classify(&mut self, e: &Expr) -> constness {
|
||||||
general_const
|
let did = ast_util::local_def(e.id);
|
||||||
|
match self.ccache.find(&did) {
|
||||||
|
Some(&x) => return x,
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
let cn = match e.node {
|
||||||
|
ast::ExprLit(lit) => {
|
||||||
|
match lit.node {
|
||||||
|
ast::lit_str(*) | ast::lit_float(*) => general_const,
|
||||||
|
_ => integral_const
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None => non_const
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ConstEvalVisitor { tcx: ty::ctxt }
|
ast::ExprUnary(_, _, inner) | ast::ExprParen(inner) =>
|
||||||
|
self.classify(inner),
|
||||||
|
|
||||||
|
ast::ExprBinary(_, _, a, b) =>
|
||||||
|
join(self.classify(a), self.classify(b)),
|
||||||
|
|
||||||
|
ast::ExprTup(ref es) |
|
||||||
|
ast::ExprVec(ref es, ast::MutImmutable) =>
|
||||||
|
join_all(es.iter().map(|e| self.classify(*e))),
|
||||||
|
|
||||||
|
ast::ExprVstore(e, vstore) => {
|
||||||
|
match vstore {
|
||||||
|
ast::ExprVstoreSlice => self.classify(e),
|
||||||
|
ast::ExprVstoreUniq |
|
||||||
|
ast::ExprVstoreBox |
|
||||||
|
ast::ExprVstoreMutBox |
|
||||||
|
ast::ExprVstoreMutSlice => non_const
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::ExprStruct(_, ref fs, None) => {
|
||||||
|
let cs = do fs.iter().map |f| {
|
||||||
|
self.classify(f.expr)
|
||||||
|
};
|
||||||
|
join_all(cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::ExprCast(base, _) => {
|
||||||
|
let ty = ty::expr_ty(self.tcx, e);
|
||||||
|
let base = self.classify(base);
|
||||||
|
if ty::type_is_integral(ty) {
|
||||||
|
join(integral_const, base)
|
||||||
|
} else if ty::type_is_fp(ty) {
|
||||||
|
join(general_const, base)
|
||||||
|
} else {
|
||||||
|
non_const
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::ExprField(base, _, _) => self.classify(base),
|
||||||
|
|
||||||
|
ast::ExprIndex(_, base, idx) =>
|
||||||
|
join(self.classify(base), self.classify(idx)),
|
||||||
|
|
||||||
|
ast::ExprAddrOf(ast::MutImmutable, base) => self.classify(base),
|
||||||
|
|
||||||
|
// FIXME: (#3728) we can probably do something CCI-ish
|
||||||
|
// surrounding nonlocal constants. But we don't yet.
|
||||||
|
ast::ExprPath(_) => self.lookup_constness(e),
|
||||||
|
|
||||||
|
ast::ExprRepeat(*) => general_const,
|
||||||
|
|
||||||
|
_ => non_const
|
||||||
|
};
|
||||||
|
self.ccache.insert(did, cn);
|
||||||
|
cn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_constness(&self, e: &Expr) -> constness {
|
||||||
|
match lookup_const(self.tcx, e) {
|
||||||
|
Some(rhs) => {
|
||||||
|
let ty = ty::expr_ty(self.tcx, rhs);
|
||||||
|
if ty::type_is_integral(ty) {
|
||||||
|
integral_const
|
||||||
|
} else {
|
||||||
|
general_const
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => non_const
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
impl Visitor<()> for ConstEvalVisitor {
|
impl Visitor<()> for ConstEvalVisitor {
|
||||||
fn visit_expr_post(&mut self, e:@Expr, _:()) {
|
fn visit_expr_post(&mut self, e:@Expr, _:()) {
|
||||||
classify(e, self.tcx);
|
self.classify(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_crate(crate: &ast::Crate,
|
pub fn process_crate(crate: &ast::Crate,
|
||||||
tcx: ty::ctxt) {
|
tcx: ty::ctxt) {
|
||||||
let mut v = ConstEvalVisitor { tcx: tcx };
|
let mut v = ConstEvalVisitor {
|
||||||
|
tcx: tcx,
|
||||||
|
ccache: HashMap::new(),
|
||||||
|
};
|
||||||
visit::walk_crate(&mut v, crate, ());
|
visit::walk_crate(&mut v, crate, ());
|
||||||
tcx.sess.abort_if_errors();
|
tcx.sess.abort_if_errors();
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,6 @@ struct ctxt_ {
|
|||||||
freevars: freevars::freevar_map,
|
freevars: freevars::freevar_map,
|
||||||
tcache: type_cache,
|
tcache: type_cache,
|
||||||
rcache: creader_cache,
|
rcache: creader_cache,
|
||||||
ccache: constness_cache,
|
|
||||||
short_names_cache: @mut HashMap<t, @str>,
|
short_names_cache: @mut HashMap<t, @str>,
|
||||||
needs_unwind_cleanup_cache: @mut HashMap<t, bool>,
|
needs_unwind_cleanup_cache: @mut HashMap<t, bool>,
|
||||||
tc_cache: @mut HashMap<uint, TypeContents>,
|
tc_cache: @mut HashMap<uint, TypeContents>,
|
||||||
@ -346,6 +345,11 @@ struct ctxt_ {
|
|||||||
// The set of external traits whose implementations have been read. This
|
// The set of external traits whose implementations have been read. This
|
||||||
// is used for lazy resolution of traits.
|
// is used for lazy resolution of traits.
|
||||||
populated_external_traits: @mut HashSet<ast::DefId>,
|
populated_external_traits: @mut HashSet<ast::DefId>,
|
||||||
|
|
||||||
|
// These two caches are used by const_eval when decoding external statics
|
||||||
|
// and variants that are found.
|
||||||
|
extern_const_statics: @mut HashMap<ast::DefId, Option<@ast::Expr>>,
|
||||||
|
extern_const_variants: @mut HashMap<ast::DefId, Option<@ast::Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum tbox_flag {
|
pub enum tbox_flag {
|
||||||
@ -897,8 +901,6 @@ pub struct ty_param_substs_and_ty {
|
|||||||
|
|
||||||
type type_cache = @mut HashMap<ast::DefId, ty_param_bounds_and_ty>;
|
type type_cache = @mut HashMap<ast::DefId, ty_param_bounds_and_ty>;
|
||||||
|
|
||||||
type constness_cache = @mut HashMap<ast::DefId, const_eval::constness>;
|
|
||||||
|
|
||||||
pub type node_type_table = @mut HashMap<uint,t>;
|
pub type node_type_table = @mut HashMap<uint,t>;
|
||||||
|
|
||||||
fn mk_rcache() -> creader_cache {
|
fn mk_rcache() -> creader_cache {
|
||||||
@ -935,7 +937,6 @@ pub fn mk_ctxt(s: session::Session,
|
|||||||
freevars: freevars,
|
freevars: freevars,
|
||||||
tcache: @mut HashMap::new(),
|
tcache: @mut HashMap::new(),
|
||||||
rcache: mk_rcache(),
|
rcache: mk_rcache(),
|
||||||
ccache: @mut HashMap::new(),
|
|
||||||
short_names_cache: new_ty_hash(),
|
short_names_cache: new_ty_hash(),
|
||||||
needs_unwind_cleanup_cache: new_ty_hash(),
|
needs_unwind_cleanup_cache: new_ty_hash(),
|
||||||
tc_cache: @mut HashMap::new(),
|
tc_cache: @mut HashMap::new(),
|
||||||
@ -961,6 +962,9 @@ pub fn mk_ctxt(s: session::Session,
|
|||||||
impl_vtables: @mut HashMap::new(),
|
impl_vtables: @mut HashMap::new(),
|
||||||
populated_external_types: @mut HashSet::new(),
|
populated_external_types: @mut HashSet::new(),
|
||||||
populated_external_traits: @mut HashSet::new(),
|
populated_external_traits: @mut HashSet::new(),
|
||||||
|
|
||||||
|
extern_const_statics: @mut HashMap::new(),
|
||||||
|
extern_const_variants: @mut HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user