Add a memoize! macro and use it throughout rustc

This commit is contained in:
Jakub Wieczorek 2014-10-12 22:01:38 +02:00
parent 5201bf17e6
commit c2e8f3b481
3 changed files with 125 additions and 113 deletions

View File

@ -97,6 +97,7 @@ pub mod middle {
pub mod intrinsicck;
pub mod lang_items;
pub mod liveness;
pub mod macros;
pub mod mem_categorization;
pub mod pat_util;
pub mod privacy;

View File

@ -0,0 +1,71 @@
// Copyright 2014 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.
#![macro_escape]
macro_rules! memoize_expand_block(
($cache_map:expr, $cache_key:expr, $($param_name:ident: $param_ty:ty),*) => { {
match ($cache_map).borrow().find(&$cache_key) {
Some(ref result) => return (*result).clone(),
None => {}
}
let result = inner($($param_name), *);
($cache_map).borrow_mut().insert($cache_key, result.clone());
result
} }
)
/// Memoizes a function using a cache that is available by evaluating the
/// `$cache_map` exression in the context of the function's arguments.
/// `$cache_key` is the expression that will be used to compute the cache key
/// for each function invocation.
///
/// The macro assumes the cache to be a RefCell containing a HashMap,
/// which is in practice how most caching in rustc is currently carried out.
///
/// # Example
///
/// ```
/// struct Context {
/// fibonacci_cache: RefCell<HashMap<uint, uint>>
/// }
///
/// memoize!(context.fibonacci_cache, n,
/// fn fibonacci(context: &Context, n: uint) -> uint {
/// match n {
/// 0 | 1 => n,
/// _ => fibonacci(n - 2) + fibonacci(n - 1)
/// }
/// }
/// )
/// ```
macro_rules! memoize(
($cache_map:expr, $cache_key:expr,
fn $name:ident(
$($param_name:ident: $param_ty:ty),*
) -> $output_ty:ty $block:block
) => {
fn $name($($param_name: $param_ty), *) -> $output_ty {
fn inner($($param_name: $param_ty), *) -> $output_ty $block
memoize_expand_block!($cache_map, $cache_key, $($param_name: $param_ty), *)
}
};
($cache_map:expr, $cache_key:expr,
pub fn $name:ident(
$($param_name:ident: $param_ty:ty),*
) -> $output_ty:ty $block:block
) => {
pub fn $name($($param_name: $param_ty), *) -> $output_ty {
fn inner($($param_name: $param_ty), *) -> $output_ty $block
memoize_expand_block!($cache_map, $cache_key, $($param_name: $param_ty), *)
}
}
)

View File

@ -2117,22 +2117,8 @@ pub fn type_needs_drop(cx: &ctxt, ty: t) -> bool {
// task can free them all at once later. Currently only things
// that only contain scalars and shared boxes can avoid unwind
// cleanups.
pub fn type_needs_unwind_cleanup(cx: &ctxt, ty: t) -> bool {
match cx.needs_unwind_cleanup_cache.borrow().find(&ty) {
Some(&result) => return result,
None => ()
}
let mut tycache = HashSet::new();
let needs_unwind_cleanup =
type_needs_unwind_cleanup_(cx, ty, &mut tycache);
cx.needs_unwind_cleanup_cache.borrow_mut().insert(ty, needs_unwind_cleanup);
needs_unwind_cleanup
}
fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t,
tycache: &mut HashSet<t>) -> bool {
memoize!(cx.needs_unwind_cleanup_cache, ty,
fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t, tycache: &mut HashSet<t>) -> bool {
// Prevent infinite recursion
if !tycache.insert(ty) {
return false;
@ -2140,32 +2126,29 @@ fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t,
let mut needs_unwind_cleanup = false;
maybe_walk_ty(ty, |ty| {
let result = match get(ty).sty {
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
ty_tup(_) | ty_ptr(_) => {
true
}
ty_enum(did, ref substs) => {
for v in (*enum_variants(cx, did)).iter() {
for aty in v.args.iter() {
let t = aty.subst(cx, substs);
needs_unwind_cleanup |=
type_needs_unwind_cleanup_(cx, t, tycache);
}
}
!needs_unwind_cleanup
}
_ => {
needs_unwind_cleanup = true;
false
}
needs_unwind_cleanup |= match get(ty).sty {
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) |
ty_float(_) | ty_tup(_) | ty_ptr(_) => false,
ty_enum(did, ref substs) =>
enum_variants(cx, did).iter().any(|v|
v.args.iter().any(|aty| {
let t = aty.subst(cx, substs);
type_needs_unwind_cleanup_(cx, t, tycache)
})
),
_ => true
};
result
!needs_unwind_cleanup
});
needs_unwind_cleanup
}
)
pub fn type_needs_unwind_cleanup(cx: &ctxt, ty: t) -> bool {
type_needs_unwind_cleanup_(cx, ty, &mut HashSet::new())
}
/**
* Type contents is how the type checker reasons about kinds.
@ -2179,6 +2162,7 @@ fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t,
* easier for me (nmatsakis) to think about what is contained within
* a type than to think about what is *not* contained within a type.
*/
#[deriving(Clone)]
pub struct TypeContents {
pub bits: u64
}
@ -2358,19 +2342,9 @@ pub fn type_interior_is_unsafe(cx: &ctxt, t: ty::t) -> bool {
type_contents(cx, t).interior_unsafe()
}
memoize!(cx.tc_cache, type_id(ty),
pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
let ty_id = type_id(ty);
match cx.tc_cache.borrow().find(&ty_id) {
Some(tc) => { return *tc; }
None => {}
}
let mut cache = HashMap::new();
let result = tc_ty(cx, ty, &mut cache);
cx.tc_cache.borrow_mut().insert(ty_id, result);
return result;
return tc_ty(cx, ty, &mut HashMap::new());
fn tc_ty(cx: &ctxt,
ty: t,
@ -2685,6 +2659,7 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
}
}
}
)
pub fn type_moves_by_default(cx: &ctxt, ty: t) -> bool {
type_contents(cx, ty).moves_by_default(cx)
@ -4033,28 +4008,23 @@ pub fn impl_or_trait_item(cx: &ctxt, id: ast::DefId) -> ImplOrTraitItem {
/// Returns true if the given ID refers to an associated type and false if it
/// refers to anything else.
memoize!(cx.associated_types, id,
pub fn is_associated_type(cx: &ctxt, id: ast::DefId) -> bool {
let result = match cx.associated_types.borrow_mut().find(&id) {
Some(result) => return *result,
None if id.krate == ast::LOCAL_CRATE => {
match cx.impl_or_trait_items.borrow().find(&id) {
Some(ref item) => {
match **item {
TypeTraitItem(_) => true,
MethodTraitItem(_) => false,
}
if id.krate == ast::LOCAL_CRATE {
match cx.impl_or_trait_items.borrow().find(&id) {
Some(ref item) => {
match **item {
TypeTraitItem(_) => true,
MethodTraitItem(_) => false,
}
None => false,
}
None => false,
}
None => {
csearch::is_associated_type(&cx.sess.cstore, id)
}
};
cx.associated_types.borrow_mut().insert(id, result);
result
} else {
csearch::is_associated_type(&cx.sess.cstore, id)
}
}
)
/// Returns the parameter index that the given associated type corresponds to.
pub fn associated_type_parameter_index(cx: &ctxt,
@ -4110,13 +4080,9 @@ pub fn trait_item_def_ids(cx: &ctxt, id: ast::DefId)
})
}
memoize!(cx.impl_trait_cache, id,
pub fn impl_trait_ref(cx: &ctxt, id: ast::DefId) -> Option<Rc<TraitRef>> {
match cx.impl_trait_cache.borrow().find(&id) {
Some(ret) => { return ret.clone(); }
None => {}
}
let ret = if id.krate == ast::LOCAL_CRATE {
if id.krate == ast::LOCAL_CRATE {
debug!("(impl_trait_ref) searching for trait impl {:?}", id);
match cx.map.find(id.node) {
Some(ast_map::NodeItem(item)) => {
@ -4136,11 +4102,9 @@ pub fn impl_trait_ref(cx: &ctxt, id: ast::DefId) -> Option<Rc<TraitRef>> {
}
} else {
csearch::get_impl_trait(cx, id)
};
cx.impl_trait_cache.borrow_mut().insert(id, ret.clone());
ret
}
}
)
pub fn trait_ref_to_def_id(tcx: &ctxt, tr: &ast::TraitRef) -> ast::DefId {
let def = *tcx.def_map.borrow()
@ -4324,13 +4288,9 @@ pub fn type_is_empty(cx: &ctxt, t: t) -> bool {
}
}
memoize!(cx.enum_var_cache, id,
pub fn enum_variants(cx: &ctxt, id: ast::DefId) -> Rc<Vec<Rc<VariantInfo>>> {
match cx.enum_var_cache.borrow().find(&id) {
Some(variants) => return variants.clone(),
_ => { /* fallthrough */ }
}
let result = if ast::LOCAL_CRATE != id.krate {
if ast::LOCAL_CRATE != id.krate {
Rc::new(csearch::get_enum_variants(cx, id))
} else {
/*
@ -4385,12 +4345,9 @@ pub fn enum_variants(cx: &ctxt, id: ast::DefId) -> Rc<Vec<Rc<VariantInfo>>> {
}
_ => cx.sess.bug("enum_variants: id not bound to an enum")
}
};
cx.enum_var_cache.borrow_mut().insert(id, result.clone());
result
}
}
)
// Returns information about the enum variant with the given ID:
pub fn enum_variant_with_id(cx: &ctxt,
@ -4415,22 +4372,12 @@ pub fn lookup_item_type(cx: &ctxt,
}
/// Given the did of a trait, returns its canonical trait ref.
memoize!(cx.trait_defs, did,
pub fn lookup_trait_def(cx: &ctxt, did: ast::DefId) -> Rc<ty::TraitDef> {
let mut trait_defs = cx.trait_defs.borrow_mut();
match trait_defs.find_copy(&did) {
Some(trait_def) => {
// The item is in this crate. The caller should have added it to the
// type cache already
trait_def
}
None => {
assert!(did.krate != ast::LOCAL_CRATE);
let trait_def = Rc::new(csearch::get_trait_def(cx, did));
trait_defs.insert(did, trait_def.clone());
trait_def
}
}
assert!(did.krate != ast::LOCAL_CRATE);
Rc::new(csearch::get_trait_def(cx, did))
}
)
/// Given a reference to a trait, returns the bounds declared on the
/// trait, with appropriate substitutions applied.
@ -4489,13 +4436,9 @@ pub fn lookup_simd(tcx: &ctxt, did: DefId) -> bool {
}
/// Obtain the representation annotation for a struct definition.
memoize!(tcx.repr_hint_cache, did,
pub fn lookup_repr_hints(tcx: &ctxt, did: DefId) -> Rc<Vec<attr::ReprAttr>> {
match tcx.repr_hint_cache.borrow().find(&did) {
None => {}
Some(ref hints) => return (*hints).clone(),
}
let acc = if did.krate == LOCAL_CRATE {
Rc::new(if did.krate == LOCAL_CRATE {
let mut acc = Vec::new();
ty::each_attr(tcx, did, |meta| {
acc.extend(attr::find_repr_attrs(tcx.sess.diagnostic(),
@ -4505,12 +4448,9 @@ pub fn lookup_repr_hints(tcx: &ctxt, did: DefId) -> Rc<Vec<attr::ReprAttr>> {
acc
} else {
csearch::get_repr_attrs(&tcx.sess.cstore, did)
};
let acc = Rc::new(acc);
tcx.repr_hint_cache.borrow_mut().insert(did, acc.clone());
acc
})
}
)
// Look up a field ID, whether or not it's local
// Takes a list of type substs in case the struct is generic