]>
Commit | Line | Data |
---|---|---|
9cc50fc6 SL |
1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
54a0048b | 11 | use hir::def_id::DefId; |
9cc50fc6 SL |
12 | use rustc_data_structures::fnv::FnvHashMap; |
13 | use std::cell::RefCell; | |
14 | use std::ops::Index; | |
15 | use std::hash::Hash; | |
16 | use std::marker::PhantomData; | |
17 | use util::common::MemoizationMap; | |
18 | ||
19 | use super::{DepNode, DepGraph}; | |
20 | ||
21 | /// A DepTrackingMap offers a subset of the `Map` API and ensures that | |
22 | /// we make calls to `read` and `write` as appropriate. We key the | |
23 | /// maps with a unique type for brevity. | |
24 | pub struct DepTrackingMap<M: DepTrackingMapConfig> { | |
25 | phantom: PhantomData<M>, | |
26 | graph: DepGraph, | |
27 | map: FnvHashMap<M::Key, M::Value>, | |
28 | } | |
29 | ||
30 | pub trait DepTrackingMapConfig { | |
31 | type Key: Eq + Hash + Clone; | |
32 | type Value: Clone; | |
54a0048b | 33 | fn to_dep_node(key: &Self::Key) -> DepNode<DefId>; |
9cc50fc6 SL |
34 | } |
35 | ||
36 | impl<M: DepTrackingMapConfig> DepTrackingMap<M> { | |
37 | pub fn new(graph: DepGraph) -> DepTrackingMap<M> { | |
38 | DepTrackingMap { | |
39 | phantom: PhantomData, | |
40 | graph: graph, | |
41 | map: FnvHashMap() | |
42 | } | |
43 | } | |
44 | ||
45 | /// Registers a (synthetic) read from the key `k`. Usually this | |
46 | /// is invoked automatically by `get`. | |
47 | fn read(&self, k: &M::Key) { | |
48 | let dep_node = M::to_dep_node(k); | |
49 | self.graph.read(dep_node); | |
50 | } | |
51 | ||
52 | /// Registers a (synthetic) write to the key `k`. Usually this is | |
53 | /// invoked automatically by `insert`. | |
54 | fn write(&self, k: &M::Key) { | |
55 | let dep_node = M::to_dep_node(k); | |
56 | self.graph.write(dep_node); | |
57 | } | |
58 | ||
59 | pub fn get(&self, k: &M::Key) -> Option<&M::Value> { | |
60 | self.read(k); | |
61 | self.map.get(k) | |
62 | } | |
63 | ||
64 | pub fn insert(&mut self, k: M::Key, v: M::Value) -> Option<M::Value> { | |
65 | self.write(&k); | |
66 | self.map.insert(k, v) | |
67 | } | |
68 | ||
69 | pub fn contains_key(&self, k: &M::Key) -> bool { | |
70 | self.read(k); | |
71 | self.map.contains_key(k) | |
72 | } | |
73 | } | |
74 | ||
75 | impl<M: DepTrackingMapConfig> MemoizationMap for RefCell<DepTrackingMap<M>> { | |
76 | type Key = M::Key; | |
77 | type Value = M::Value; | |
78 | ||
79 | /// Memoizes an entry in the dep-tracking-map. If the entry is not | |
80 | /// already present, then `op` will be executed to compute its value. | |
81 | /// The resulting dependency graph looks like this: | |
82 | /// | |
83 | /// [op] -> Map(key) -> CurrentTask | |
84 | /// | |
85 | /// Here, `[op]` represents whatever nodes `op` reads in the | |
86 | /// course of execution; `Map(key)` represents the node for this | |
87 | /// map; and `CurrentTask` represents the current task when | |
88 | /// `memoize` is invoked. | |
89 | /// | |
90 | /// **Important:* when `op` is invoked, the current task will be | |
91 | /// switched to `Map(key)`. Therefore, if `op` makes use of any | |
92 | /// HIR nodes or shared state accessed through its closure | |
93 | /// environment, it must explicitly register a read of that | |
94 | /// state. As an example, see `type_scheme_of_item` in `collect`, | |
95 | /// which looks something like this: | |
96 | /// | |
97 | /// ``` | |
98 | /// fn type_scheme_of_item(..., item: &hir::Item) -> ty::TypeScheme<'tcx> { | |
99 | /// let item_def_id = ccx.tcx.map.local_def_id(it.id); | |
100 | /// ccx.tcx.tcache.memoized(item_def_id, || { | |
101 | /// ccx.tcx.dep_graph.read(DepNode::Hir(item_def_id)); // (*) | |
102 | /// compute_type_scheme_of_item(ccx, item) | |
103 | /// }); | |
104 | /// } | |
105 | /// ``` | |
106 | /// | |
107 | /// The key is the line marked `(*)`: the closure implicitly | |
108 | /// accesses the body of the item `item`, so we register a read | |
109 | /// from `Hir(item_def_id)`. | |
110 | fn memoize<OP>(&self, key: M::Key, op: OP) -> M::Value | |
111 | where OP: FnOnce() -> M::Value | |
112 | { | |
113 | let graph; | |
114 | { | |
115 | let this = self.borrow(); | |
116 | if let Some(result) = this.map.get(&key) { | |
117 | this.read(&key); | |
118 | return result.clone(); | |
119 | } | |
120 | graph = this.graph.clone(); | |
121 | } | |
122 | ||
123 | let _task = graph.in_task(M::to_dep_node(&key)); | |
124 | let result = op(); | |
125 | self.borrow_mut().map.insert(key, result.clone()); | |
126 | result | |
127 | } | |
128 | } | |
129 | ||
130 | impl<'k, M: DepTrackingMapConfig> Index<&'k M::Key> for DepTrackingMap<M> { | |
131 | type Output = M::Value; | |
132 | ||
133 | #[inline] | |
134 | fn index(&self, k: &'k M::Key) -> &M::Value { | |
135 | self.get(k).unwrap() | |
136 | } | |
137 | } | |
138 |