/rust/registry/src/index.crates.io-6f17d22bba15001f/egg-0.6.0/src/egraph.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use std::collections::HashMap; |
2 | | use std::{ |
3 | | borrow::BorrowMut, |
4 | | fmt::{self, Debug}, |
5 | | }; |
6 | | |
7 | | use indexmap::IndexMap; |
8 | | use log::*; |
9 | | |
10 | | use crate::{ |
11 | | Analysis, AstSize, Dot, EClass, Extractor, Id, Language, Pattern, RecExpr, Searcher, UnionFind, |
12 | | }; |
13 | | |
14 | | /** A data structure to keep track of equalities between expressions. |
15 | | |
16 | | # What's an egraph? |
17 | | |
18 | | An egraph ([/'igraf/][sound]) is a data structure to maintain equivalence |
19 | | |
20 | | classes of expressions. |
21 | | An egraph conceptually is a set of eclasses, each of which |
22 | | contains equivalent enodes. |
23 | | An enode is conceptually and operator with children, but instead of |
24 | | children being other operators or values, the children are eclasses. |
25 | | |
26 | | In `egg`, these are respresented by the [`EGraph`], [`EClass`], and |
27 | | [`Language`] (enodes) types. |
28 | | |
29 | | |
30 | | Here's an egraph created and rendered by [this example](struct.Dot.html). |
31 | | As described in the documentation for [egraph visualization][dot] and |
32 | | in the academic literature, we picture eclasses as dotted boxes |
33 | | surrounding the equivalent enodes: |
34 | | |
35 | | <img src="https://mwillsey.com/assets/simple-egraph.svg"/> |
36 | | |
37 | | We say a term _t_ is _represented_ in an eclass _e_ if you can pick a |
38 | | single enode from each eclass such that _t_ is in _e_. |
39 | | A term is represented in the egraph if it's represented in any eclass. |
40 | | In the image above, the terms `2 * a`, `a * 2`, and `a << 1` are all |
41 | | represented in the same eclass and thus are equivalent. |
42 | | The terms `1`, `(a * 2) / 2`, and `(a << 1) / 2` are represented in |
43 | | the egraph, but not in the same eclass as the prior three terms, so |
44 | | these three are not equivalent to those three. |
45 | | |
46 | | Egraphs are useful when you have a bunch of very similar expressions, |
47 | | some of which are equivalent, and you'd like a compactly store them. |
48 | | This compactness allows rewrite systems based on egraphs to |
49 | | efficiently "remember" the expression before and after rewriting, so |
50 | | you can essentially apply all rewrites at once. |
51 | | See [`Rewrite`] and [`Runner`] for more details about rewrites and |
52 | | running rewrite systems, respectively. |
53 | | |
54 | | # Invariants and Rebuilding |
55 | | |
56 | | An egraph has two core operations that modify the egraph: |
57 | | [`add`] which adds enodes to the egraph, and |
58 | | [`union`] which merges two eclasses. |
59 | | These operations maintains two key (related) invariants: |
60 | | |
61 | | 1. **Uniqueness of enodes** |
62 | | |
63 | | There do not exist two distinct enodes with equal operators and equal |
64 | | children in the eclass, either in the same eclass or different eclasses. |
65 | | This is maintained in part by the hashconsing performed by [`add`], |
66 | | and by deduplication performed by [`union`] and [`rebuild`]. |
67 | | |
68 | | 2. **Congruence closure** |
69 | | |
70 | | An egraph maintains not just an [equivalence relation] over |
71 | | expressions, but a [congruence relation]. |
72 | | So as the user calls [`union`], many eclasses other than the given |
73 | | two may need to merge to maintain congruence. |
74 | | |
75 | | For example, suppose terms `a + x` and `a + y` are represented in |
76 | | eclasses 1 and 2, respectively. |
77 | | At some later point, `x` and `y` become |
78 | | equivalent (perhaps the user called [`union`] on their containing |
79 | | eclasses). |
80 | | Eclasses 1 and 2 must merge, because now the two `+` |
81 | | operators have equivalent arguments, making them equivalent. |
82 | | |
83 | | `egg` takes a delayed approach to maintaining these invariants. |
84 | | Specifically, the effects of calling [`union`] (or applying a rewrite, |
85 | | which calls [`union`]) may not be reflected immediately. |
86 | | To restore the egraph invariants and make these effects visible, the |
87 | | user *must* call the [`rebuild`] method. |
88 | | |
89 | | `egg`s choice here allows for a higher performance implementation. |
90 | | Maintaining the congruence relation complicates the core egraph data |
91 | | structure and requires an expensive traversal through the egraph on |
92 | | every [`union`]. |
93 | | `egg` chooses to relax these invariants for better performance, only |
94 | | restoring the invariants on a call to [`rebuild`]. |
95 | | See the [`rebuild`] documentation for more information. |
96 | | Note also that [`Runner`]s take care of this for you, calling |
97 | | [`rebuild`] between rewrite iterations. |
98 | | |
99 | | # egraphs in `egg` |
100 | | |
101 | | In `egg`, the main types associated with egraphs are |
102 | | [`EGraph`], [`EClass`], [`Language`], and [`Id`]. |
103 | | |
104 | | [`EGraph`] and [`EClass`] are all generic over a |
105 | | [`Language`], meaning that types actually floating around in the |
106 | | egraph are all user-defined. |
107 | | In particular, the enodes are elements of your [`Language`]. |
108 | | [`EGraph`]s and [`EClass`]es are additionally parameterized by some |
109 | | [`Analysis`], abritrary data associated with each eclass. |
110 | | |
111 | | Many methods of [`EGraph`] deal with [`Id`]s, which represent eclasses. |
112 | | Because eclasses are frequently merged, many [`Id`]s will refer to the |
113 | | same eclass. |
114 | | |
115 | | [`EGraph`]: struct.EGraph.html |
116 | | [`EClass`]: struct.EClass.html |
117 | | [`Rewrite`]: struct.Rewrite.html |
118 | | [`Runner`]: struct.Runner.html |
119 | | [`Language`]: trait.Language.html |
120 | | [`Analysis`]: trait.Analysis.html |
121 | | [`Id`]: struct.Id.html |
122 | | [`add`]: struct.EGraph.html#method.add |
123 | | [`union`]: struct.EGraph.html#method.union |
124 | | [`rebuild`]: struct.EGraph.html#method.rebuild |
125 | | [equivalence relation]: https://en.wikipedia.org/wiki/Equivalence_relation |
126 | | [congruence relation]: https://en.wikipedia.org/wiki/Congruence_relation |
127 | | [dot]: struct.Dot.html |
128 | | [extract]: struct.Extractor.html |
129 | | [sound]: https://itinerarium.github.io/phoneme-synthesis/?w=/'igraf/ |
130 | | **/ |
131 | 2.68k | #[derive(Clone)] Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::clone::Clone>::clone Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::clone::Clone>::clone <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::clone::Clone>::clone Line | Count | Source | 131 | 2.68k | #[derive(Clone)] |
Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::clone::Clone>::clone |
132 | | pub struct EGraph<L: Language, N: Analysis<L>> { |
133 | | /// The `Analysis` given when creating this `EGraph`. |
134 | | pub analysis: N, |
135 | | memo: HashMap<L, Id>, |
136 | | unionfind: UnionFind, |
137 | | classes: SparseVec<EClass<L, N::Data>>, |
138 | | dirty_unions: Vec<Id>, |
139 | | repairs_since_rebuild: usize, |
140 | | pub(crate) classes_by_op: IndexMap<std::mem::Discriminant<L>, indexmap::IndexSet<Id>>, |
141 | | } |
142 | | |
143 | | type SparseVec<T> = Vec<Option<Box<T>>>; |
144 | | |
145 | | impl<L: Language, N: Analysis<L> + Default> Default for EGraph<L, N> { |
146 | 0 | fn default() -> Self { |
147 | 0 | Self::new(N::default()) |
148 | 0 | } Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::default::Default>::default Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::default::Default>::default |
149 | | } |
150 | | |
151 | | // manual debug impl to avoid L: Language bound on EGraph defn |
152 | | impl<L: Language, N: Analysis<L>> Debug for EGraph<L, N> { |
153 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
154 | 0 | f.debug_struct("EGraph") |
155 | 0 | .field("memo", &self.memo) |
156 | 0 | .field("classes", &self.classes) |
157 | 0 | .finish() |
158 | 0 | } Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::fmt::Debug>::fmt Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::fmt::Debug>::fmt |
159 | | } |
160 | | |
161 | | impl<L: Language, N: Analysis<L>> EGraph<L, N> { |
162 | | /// Creates a new, empty `EGraph` with the given `Analysis` |
163 | 2.68k | pub fn new(analysis: N) -> Self { |
164 | 2.68k | Self { |
165 | 2.68k | analysis, |
166 | 2.68k | memo: Default::default(), |
167 | 2.68k | classes: Default::default(), |
168 | 2.68k | unionfind: Default::default(), |
169 | 2.68k | dirty_unions: Default::default(), |
170 | 2.68k | classes_by_op: IndexMap::default(), |
171 | 2.68k | repairs_since_rebuild: 0, |
172 | 2.68k | } |
173 | 2.68k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::new Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::new <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::new Line | Count | Source | 163 | 2.68k | pub fn new(analysis: N) -> Self { | 164 | 2.68k | Self { | 165 | 2.68k | analysis, | 166 | 2.68k | memo: Default::default(), | 167 | 2.68k | classes: Default::default(), | 168 | 2.68k | unionfind: Default::default(), | 169 | 2.68k | dirty_unions: Default::default(), | 170 | 2.68k | classes_by_op: IndexMap::default(), | 171 | 2.68k | repairs_since_rebuild: 0, | 172 | 2.68k | } | 173 | 2.68k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::new |
174 | | |
175 | | /// Returns an iterator over the eclasses in the egraph. |
176 | 282k | pub fn classes(&self) -> impl Iterator<Item = &EClass<L, N::Data>> { |
177 | 282k | self.classes |
178 | 282k | .iter() |
179 | 282k | .filter_map(Option::as_ref) |
180 | 282k | .map(AsRef::as_ref) |
181 | 282k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::classes Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::classes <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::classes Line | Count | Source | 176 | 282k | pub fn classes(&self) -> impl Iterator<Item = &EClass<L, N::Data>> { | 177 | 282k | self.classes | 178 | 282k | .iter() | 179 | 282k | .filter_map(Option::as_ref) | 180 | 282k | .map(AsRef::as_ref) | 181 | 282k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::classes |
182 | | |
183 | | /// Returns an mutating iterator over the eclasses in the egraph. |
184 | 0 | pub fn classes_mut(&mut self) -> impl Iterator<Item = &mut EClass<L, N::Data>> { |
185 | 0 | self.classes |
186 | 0 | .iter_mut() |
187 | 0 | .filter_map(Option::as_mut) |
188 | 0 | .map(AsMut::as_mut) |
189 | 0 | } Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::classes_mut Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::classes_mut |
190 | | |
191 | | /// Returns `true` if the egraph is empty |
192 | | /// # Example |
193 | | /// ``` |
194 | | /// use egg::{*, SymbolLang as S}; |
195 | | /// let mut egraph = EGraph::<S, ()>::default(); |
196 | | /// assert!(egraph.is_empty()); |
197 | | /// egraph.add(S::leaf("foo")); |
198 | | /// assert!(!egraph.is_empty()); |
199 | | /// ``` |
200 | 0 | pub fn is_empty(&self) -> bool { |
201 | 0 | self.memo.is_empty() |
202 | 0 | } Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::is_empty Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::is_empty |
203 | | |
204 | | /// Returns the number of enodes in the `EGraph`. |
205 | | /// |
206 | | /// Actually returns the size of the hashcons index. |
207 | | /// # Example |
208 | | /// ``` |
209 | | /// use egg::{*, SymbolLang as S}; |
210 | | /// let mut egraph = EGraph::<S, ()>::default(); |
211 | | /// let x = egraph.add(S::leaf("x")); |
212 | | /// let y = egraph.add(S::leaf("y")); |
213 | | /// // only one eclass |
214 | | /// egraph.union(x, y); |
215 | | /// egraph.rebuild(); |
216 | | /// |
217 | | /// assert_eq!(egraph.total_size(), 2); |
218 | | /// assert_eq!(egraph.number_of_classes(), 1); |
219 | | /// ``` |
220 | 710k | pub fn total_size(&self) -> usize { |
221 | 710k | self.memo.len() |
222 | 710k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::total_size Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::total_size <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::total_size Line | Count | Source | 220 | 710k | pub fn total_size(&self) -> usize { | 221 | 710k | self.memo.len() | 222 | 710k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::total_size |
223 | | |
224 | | /// Iterates over the classes, returning the total number of nodes. |
225 | 5.37k | pub fn total_number_of_nodes(&self) -> usize { |
226 | 34.0k | self.classes().map(|c| c.len()).sum() Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::total_number_of_nodes::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::total_number_of_nodes::{closure#0}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::total_number_of_nodes::{closure#0}Line | Count | Source | 226 | 34.0k | self.classes().map(|c| c.len()).sum() |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::total_number_of_nodes::{closure#0} |
227 | 5.37k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::total_number_of_nodes Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::total_number_of_nodes <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::total_number_of_nodes Line | Count | Source | 225 | 5.37k | pub fn total_number_of_nodes(&self) -> usize { | 226 | 5.37k | self.classes().map(|c| c.len()).sum() | 227 | 5.37k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::total_number_of_nodes |
228 | | |
229 | | /// Returns the number of eclasses in the egraph. |
230 | 10.7k | pub fn number_of_classes(&self) -> usize { |
231 | 10.7k | self.classes().count() |
232 | 10.7k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::number_of_classes Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::number_of_classes <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::number_of_classes Line | Count | Source | 230 | 10.7k | pub fn number_of_classes(&self) -> usize { | 231 | 10.7k | self.classes().count() | 232 | 10.7k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::number_of_classes |
233 | | |
234 | | /// Canonicalizes an eclass id. |
235 | | /// |
236 | | /// This corresponds to the `find` operation on the egraph's |
237 | | /// underlying unionfind data structure. |
238 | | /// |
239 | | /// # Example |
240 | | /// ``` |
241 | | /// use egg::{*, SymbolLang as S}; |
242 | | /// let mut egraph = EGraph::<S, ()>::default(); |
243 | | /// let x = egraph.add(S::leaf("x")); |
244 | | /// let y = egraph.add(S::leaf("y")); |
245 | | /// assert_ne!(egraph.find(x), egraph.find(y)); |
246 | | /// |
247 | | /// egraph.union(x, y); |
248 | | /// assert_eq!(egraph.find(x), egraph.find(y)); |
249 | | /// ``` |
250 | 15.7M | pub fn find(&self, id: Id) -> Id { |
251 | 15.7M | self.unionfind.find(id) |
252 | 15.7M | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::find Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::find <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::find Line | Count | Source | 250 | 15.7M | pub fn find(&self, id: Id) -> Id { | 251 | 15.7M | self.unionfind.find(id) | 252 | 15.7M | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::find |
253 | | |
254 | | /// Creates a [`Dot`] to visualize this egraph. See [`Dot`]. |
255 | | /// |
256 | | /// [`Dot`]: struct.Dot.html |
257 | 0 | pub fn dot(&self) -> Dot<L, N> { |
258 | 0 | Dot { egraph: self } |
259 | 0 | } Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::dot Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::dot |
260 | | } |
261 | | |
262 | | impl<L: Language, N: Analysis<L>> std::ops::Index<Id> for EGraph<L, N> { |
263 | | type Output = EClass<L, N::Data>; |
264 | 2.68M | fn index(&self, id: Id) -> &Self::Output { |
265 | 2.68M | let id = self.find(id); |
266 | 2.68M | self.classes[usize::from(id)] |
267 | 2.68M | .as_ref() |
268 | 2.68M | .unwrap_or_else(|| panic!("Invalid id {}", id))Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::ops::index::Index<egg::Id>>::index::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::ops::index::Index<egg::Id>>::index::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::ops::index::Index<egg::Id>>::index::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::ops::index::Index<egg::Id>>::index::{closure#0} |
269 | 2.68M | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::ops::index::Index<egg::Id>>::index Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::ops::index::Index<egg::Id>>::index <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::ops::index::Index<egg::Id>>::index Line | Count | Source | 264 | 2.68M | fn index(&self, id: Id) -> &Self::Output { | 265 | 2.68M | let id = self.find(id); | 266 | 2.68M | self.classes[usize::from(id)] | 267 | 2.68M | .as_ref() | 268 | 2.68M | .unwrap_or_else(|| panic!("Invalid id {}", id)) | 269 | 2.68M | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::ops::index::Index<egg::Id>>::index |
270 | | } |
271 | | |
272 | | impl<L: Language, N: Analysis<L>> std::ops::IndexMut<Id> for EGraph<L, N> { |
273 | 1.05M | fn index_mut(&mut self, id: Id) -> &mut Self::Output { |
274 | 1.05M | let id = self.find(id); |
275 | 1.05M | self.classes[usize::from(id)] |
276 | 1.05M | .as_mut() |
277 | 1.05M | .unwrap_or_else(|| panic!("Invalid id {}", id))Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::ops::index::IndexMut<egg::Id>>::index_mut::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::ops::index::IndexMut<egg::Id>>::index_mut::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::ops::index::IndexMut<egg::Id>>::index_mut::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::ops::index::IndexMut<egg::Id>>::index_mut::{closure#0} |
278 | 1.05M | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::ops::index::IndexMut<egg::Id>>::index_mut Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::ops::index::IndexMut<egg::Id>>::index_mut <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::ops::index::IndexMut<egg::Id>>::index_mut Line | Count | Source | 273 | 1.05M | fn index_mut(&mut self, id: Id) -> &mut Self::Output { | 274 | 1.05M | let id = self.find(id); | 275 | 1.05M | self.classes[usize::from(id)] | 276 | 1.05M | .as_mut() | 277 | 1.05M | .unwrap_or_else(|| panic!("Invalid id {}", id)) | 278 | 1.05M | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _> as core::ops::index::IndexMut<egg::Id>>::index_mut |
279 | | } |
280 | | |
281 | | impl<L: Language, N: Analysis<L>> EGraph<L, N> { |
282 | | /// Adds a [`RecExpr`] to the [`EGraph`]. |
283 | | /// |
284 | | /// # Example |
285 | | /// ``` |
286 | | /// use egg::{*, SymbolLang as S}; |
287 | | /// let mut egraph = EGraph::<S, ()>::default(); |
288 | | /// let x = egraph.add(S::leaf("x")); |
289 | | /// let y = egraph.add(S::leaf("y")); |
290 | | /// let plus = egraph.add(S::new("+", vec![x, y])); |
291 | | /// let plus_recexpr = "(+ x y)".parse().unwrap(); |
292 | | /// assert_eq!(plus, egraph.add_expr(&plus_recexpr)); |
293 | | /// ``` |
294 | | /// |
295 | | /// [`EGraph`]: struct.EGraph.html |
296 | | /// [`RecExpr`]: struct.RecExpr.html |
297 | | /// [`add_expr`]: struct.EGraph.html#method.add_expr |
298 | 5.37k | pub fn add_expr(&mut self, expr: &RecExpr<L>) -> Id { |
299 | 5.37k | self.add_expr_rec(expr.as_ref()) |
300 | 5.37k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add_expr Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add_expr <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add_expr Line | Count | Source | 298 | 5.37k | pub fn add_expr(&mut self, expr: &RecExpr<L>) -> Id { | 299 | 5.37k | self.add_expr_rec(expr.as_ref()) | 300 | 5.37k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add_expr |
301 | | |
302 | 92.9k | fn add_expr_rec(&mut self, expr: &[L]) -> Id { |
303 | 92.9k | log::trace!("Adding expr {:?}", expr); |
304 | 92.9k | let e = expr.last().unwrap().clone().map_children(|i| { |
305 | 87.6k | let child = &expr[..usize::from(i) + 1]; |
306 | 87.6k | self.add_expr_rec(child) |
307 | 92.9k | }); Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add_expr_rec::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add_expr_rec::{closure#0}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add_expr_rec::{closure#0}Line | Count | Source | 304 | 87.6k | let e = expr.last().unwrap().clone().map_children(|i| { | 305 | 87.6k | let child = &expr[..usize::from(i) + 1]; | 306 | 87.6k | self.add_expr_rec(child) | 307 | 87.6k | }); |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add_expr_rec::{closure#0} |
308 | 92.9k | let id = self.add(e); |
309 | 92.9k | log::trace!("Added!! expr {:?}", expr); |
310 | 92.9k | id |
311 | 92.9k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add_expr_rec Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add_expr_rec <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add_expr_rec Line | Count | Source | 302 | 92.9k | fn add_expr_rec(&mut self, expr: &[L]) -> Id { | 303 | 92.9k | log::trace!("Adding expr {:?}", expr); | 304 | 92.9k | let e = expr.last().unwrap().clone().map_children(|i| { | 305 | | let child = &expr[..usize::from(i) + 1]; | 306 | | self.add_expr_rec(child) | 307 | 92.9k | }); | 308 | 92.9k | let id = self.add(e); | 309 | 92.9k | log::trace!("Added!! expr {:?}", expr); | 310 | 92.9k | id | 311 | 92.9k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add_expr_rec |
312 | | |
313 | | /// Lookup the eclass of the given enode. |
314 | | /// |
315 | | /// You can pass in either an owned enode or a `&mut` enode, |
316 | | /// in which case the enode's children will be canonicalized. |
317 | | /// |
318 | | /// # Example |
319 | | /// ``` |
320 | | /// # use egg::*; |
321 | | /// let mut egraph: EGraph<SymbolLang, ()> = Default::default(); |
322 | | /// let a = egraph.add(SymbolLang::leaf("a")); |
323 | | /// let b = egraph.add(SymbolLang::leaf("b")); |
324 | | /// let c = egraph.add(SymbolLang::leaf("c")); |
325 | | /// |
326 | | /// // lookup will find this node if its in the egraph |
327 | | /// let mut node_f_ac = SymbolLang::new("f", vec![a, c]); |
328 | | /// assert_eq!(egraph.lookup(node_f_ac.clone()), None); |
329 | | /// let id = egraph.add(node_f_ac.clone()); |
330 | | /// assert_eq!(egraph.lookup(node_f_ac.clone()), Some(id)); |
331 | | /// |
332 | | /// // if the query node isn't canonical, and its passed in by &mut instead of owned, |
333 | | /// // its children will be canonicalized |
334 | | /// egraph.union(b, c); |
335 | | /// egraph.rebuild(); |
336 | | /// assert_eq!(egraph.lookup(&mut node_f_ac), Some(id)); |
337 | | /// assert_eq!(node_f_ac, SymbolLang::new("f", vec![a, b])); |
338 | | /// ``` |
339 | 1.62M | pub fn lookup<B>(&self, mut enode: B) -> Option<Id> |
340 | 1.62M | where |
341 | 1.62M | B: BorrowMut<L>, |
342 | 1.62M | { |
343 | 1.62M | let enode = enode.borrow_mut(); |
344 | 1.62M | enode.update_children(|id| self.find(id)); Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::lookup::<&mut wasm_mutate::mutators::peephole::eggsy::lang::Lang>::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::lookup::<_>::{closure#0}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::lookup::<&mut wasm_mutate::mutators::peephole::eggsy::lang::Lang>::{closure#0}Line | Count | Source | 344 | 1.56M | enode.update_children(|id| self.find(id)); |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::lookup::<_>::{closure#0} |
345 | 1.62M | let id = self.memo.get(enode); |
346 | 1.62M | id.map(|&id| self.find(id)) Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::lookup::<&mut wasm_mutate::mutators::peephole::eggsy::lang::Lang>::{closure#1}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::lookup::<_>::{closure#1}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::lookup::<&mut wasm_mutate::mutators::peephole::eggsy::lang::Lang>::{closure#1}Line | Count | Source | 346 | 1.04M | id.map(|&id| self.find(id)) |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::lookup::<_>::{closure#1} |
347 | 1.62M | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::lookup::<&mut wasm_mutate::mutators::peephole::eggsy::lang::Lang> Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::lookup::<_> <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::lookup::<&mut wasm_mutate::mutators::peephole::eggsy::lang::Lang> Line | Count | Source | 339 | 1.62M | pub fn lookup<B>(&self, mut enode: B) -> Option<Id> | 340 | 1.62M | where | 341 | 1.62M | B: BorrowMut<L>, | 342 | 1.62M | { | 343 | 1.62M | let enode = enode.borrow_mut(); | 344 | 1.62M | enode.update_children(|id| self.find(id)); | 345 | 1.62M | let id = self.memo.get(enode); | 346 | 1.62M | id.map(|&id| self.find(id)) | 347 | 1.62M | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::lookup::<_> |
348 | | |
349 | | /// Adds an enode to the [`EGraph`]. |
350 | | /// |
351 | | /// When adding an enode, to the egraph, [`add`] it performs |
352 | | /// _hashconsing_ (sometimes called interning in other contexts). |
353 | | /// |
354 | | /// Hashconsing ensures that only one copy of that enode is in the egraph. |
355 | | /// If a copy is in the egraph, then [`add`] simply returns the id of the |
356 | | /// eclass in which the enode was found. |
357 | | /// Otherwise |
358 | | /// |
359 | | /// [`EGraph`]: struct.EGraph.html |
360 | | /// [`EClass`]: struct.EClass.html |
361 | | /// [`add`]: struct.EGraph.html#method.add |
362 | 1.62M | pub fn add(&mut self, mut enode: L) -> Id { |
363 | 1.62M | self.lookup(&mut enode).unwrap_or_else(|| { |
364 | 578k | let id = self.unionfind.make_set(); |
365 | 578k | log::trace!(" ...adding to {}", id); |
366 | 578k | let class = Box::new(EClass { |
367 | 578k | id, |
368 | 578k | nodes: vec![enode.clone()], |
369 | 578k | data: N::make(self, &enode), |
370 | 578k | parents: Default::default(), |
371 | 578k | }); |
372 | 578k | |
373 | 578k | // add this enode to the parent lists of its children |
374 | 1.04M | enode.for_each(|child| { |
375 | 1.04M | let tup = (enode.clone(), id); |
376 | 1.04M | self[child].parents.push(tup); |
377 | 1.04M | }); Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add::{closure#0}::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add::{closure#0}::{closure#0}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add::{closure#0}::{closure#0}Line | Count | Source | 374 | 1.04M | enode.for_each(|child| { | 375 | 1.04M | let tup = (enode.clone(), id); | 376 | 1.04M | self[child].parents.push(tup); | 377 | 1.04M | }); |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add::{closure#0}::{closure#0} |
378 | 578k | |
379 | 578k | assert_eq!(self.classes.len(), usize::from(id)); |
380 | 578k | self.classes.push(Some(class)); |
381 | 578k | assert!(self.memo.insert(enode, id).is_none()); |
382 | | |
383 | 578k | N::modify(self, id); |
384 | 578k | id |
385 | 1.62M | }) Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add::{closure#0}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add::{closure#0}Line | Count | Source | 363 | 578k | self.lookup(&mut enode).unwrap_or_else(|| { | 364 | 578k | let id = self.unionfind.make_set(); | 365 | 578k | log::trace!(" ...adding to {}", id); | 366 | 578k | let class = Box::new(EClass { | 367 | 578k | id, | 368 | 578k | nodes: vec![enode.clone()], | 369 | 578k | data: N::make(self, &enode), | 370 | 578k | parents: Default::default(), | 371 | 578k | }); | 372 | 578k | | 373 | 578k | // add this enode to the parent lists of its children | 374 | 578k | enode.for_each(|child| { | 375 | | let tup = (enode.clone(), id); | 376 | | self[child].parents.push(tup); | 377 | 578k | }); | 378 | 578k | | 379 | 578k | assert_eq!(self.classes.len(), usize::from(id)); | 380 | 578k | self.classes.push(Some(class)); | 381 | 578k | assert!(self.memo.insert(enode, id).is_none()); | 382 | | | 383 | 578k | N::modify(self, id); | 384 | 578k | id | 385 | 578k | }) |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add::{closure#0} |
386 | 1.62M | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::add Line | Count | Source | 362 | 1.62M | pub fn add(&mut self, mut enode: L) -> Id { | 363 | 1.62M | self.lookup(&mut enode).unwrap_or_else(|| { | 364 | | let id = self.unionfind.make_set(); | 365 | | log::trace!(" ...adding to {}", id); | 366 | | let class = Box::new(EClass { | 367 | | id, | 368 | | nodes: vec![enode.clone()], | 369 | | data: N::make(self, &enode), | 370 | | parents: Default::default(), | 371 | | }); | 372 | | | 373 | | // add this enode to the parent lists of its children | 374 | | enode.for_each(|child| { | 375 | | let tup = (enode.clone(), id); | 376 | | self[child].parents.push(tup); | 377 | | }); | 378 | | | 379 | | assert_eq!(self.classes.len(), usize::from(id)); | 380 | | self.classes.push(Some(class)); | 381 | | assert!(self.memo.insert(enode, id).is_none()); | 382 | | | 383 | | N::modify(self, id); | 384 | | id | 385 | 1.62M | }) | 386 | 1.62M | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::add |
387 | | |
388 | | /// Checks whether two [`RecExpr`]s are equivalent. |
389 | | /// Returns a list of id where both expression are represented. |
390 | | /// In most cases, there will none or exactly one id. |
391 | | /// |
392 | | /// [`RecExpr`]: struct.RecExpr.html |
393 | 0 | pub fn equivs(&self, expr1: &RecExpr<L>, expr2: &RecExpr<L>) -> Vec<Id> { |
394 | 0 | let matches1 = Pattern::from(expr1.as_ref()).search(self); |
395 | 0 | trace!("Matches1: {:?}", matches1); |
396 | | |
397 | 0 | let matches2 = Pattern::from(expr2.as_ref()).search(self); |
398 | 0 | trace!("Matches2: {:?}", matches2); |
399 | | |
400 | 0 | let mut equiv_eclasses = Vec::new(); |
401 | | |
402 | 0 | for m1 in &matches1 { |
403 | 0 | for m2 in &matches2 { |
404 | 0 | if self.find(m1.eclass) == self.find(m2.eclass) { |
405 | 0 | equiv_eclasses.push(m1.eclass) |
406 | 0 | } |
407 | | } |
408 | | } |
409 | | |
410 | 0 | equiv_eclasses |
411 | 0 | } Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::equivs Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::equivs |
412 | | |
413 | | /// Panic if the given eclass doesn't contain the given patterns |
414 | | /// |
415 | | /// Useful for testing. |
416 | 0 | pub fn check_goals(&self, id: Id, goals: &[Pattern<L>]) { |
417 | 0 | let (cost, best) = Extractor::new(self, AstSize).find_best(id); |
418 | 0 | println!("End ({}): {}", cost, best.pretty(80)); |
419 | | |
420 | 0 | for (i, goal) in goals.iter().enumerate() { |
421 | 0 | println!("Trying to prove goal {}: {}", i, goal.pretty(40)); |
422 | 0 | let matches = goal.search_eclass(&self, id); |
423 | 0 | if matches.is_none() { |
424 | 0 | let best = Extractor::new(&self, AstSize).find_best(id).1; |
425 | 0 | panic!( |
426 | 0 | "Could not prove goal {}:\n{}\nBest thing found:\n{}", |
427 | 0 | i, |
428 | 0 | goal.pretty(40), |
429 | 0 | best.pretty(40), |
430 | 0 | ); |
431 | 0 | } |
432 | | } |
433 | 0 | } Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::check_goals Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::check_goals |
434 | | |
435 | | #[inline] |
436 | 1.46M | fn union_impl(&mut self, id1: Id, id2: Id) -> (Id, bool) { |
437 | 1.46M | fn concat<T>(to: &mut Vec<T>, mut from: Vec<T>) { |
438 | 1.12M | if to.len() < from.len() { |
439 | 1.46M | std::mem::swap(to, &mut from) |
440 | 1.46M | } |
441 | 1.46M | to.extend(from); |
442 | 1.12M | } Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::union_impl::concat::<wasm_mutate::mutators::peephole::eggsy::lang::Lang> Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::union_impl::concat::<(wasm_mutate::mutators::peephole::eggsy::lang::Lang, egg::Id)> Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::union_impl::concat::<_> <egg::egraph::EGraph<_, _>>::union_impl::concat::<wasm_mutate::mutators::peephole::eggsy::lang::Lang> Line | Count | Source | 437 | 561k | fn concat<T>(to: &mut Vec<T>, mut from: Vec<T>) { | 438 | 561k | if to.len() < from.len() { | 439 | 0 | std::mem::swap(to, &mut from) | 440 | 561k | } | 441 | 561k | to.extend(from); | 442 | 561k | } |
<egg::egraph::EGraph<_, _>>::union_impl::concat::<(wasm_mutate::mutators::peephole::eggsy::lang::Lang, egg::Id)> Line | Count | Source | 437 | 561k | fn concat<T>(to: &mut Vec<T>, mut from: Vec<T>) { | 438 | 561k | if to.len() < from.len() { | 439 | 407 | std::mem::swap(to, &mut from) | 440 | 560k | } | 441 | 561k | to.extend(from); | 442 | 561k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::union_impl::concat::<_> |
443 | 1.46M | |
444 | 1.46M | let (to, from) = self.unionfind.union(id1, id2); |
445 | 0 | debug_assert_eq!(to, self.find(id1)); |
446 | 0 | debug_assert_eq!(to, self.find(id2)); |
447 | 1.46M | if to != from { |
448 | 561k | self.dirty_unions.push(to); |
449 | 561k | |
450 | 561k | // update the classes data structure |
451 | 561k | let from_class = self.classes[usize::from(from)].take().unwrap(); |
452 | 561k | let to_class = self.classes[usize::from(to)].as_mut().unwrap(); |
453 | 561k | |
454 | 561k | self.analysis.merge(&mut to_class.data, from_class.data); |
455 | 561k | concat(&mut to_class.nodes, from_class.nodes); |
456 | 561k | concat(&mut to_class.parents, from_class.parents); |
457 | 561k | |
458 | 561k | N::modify(self, to); |
459 | 900k | } |
460 | 1.46M | (to, to != from) |
461 | 1.46M | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::union_impl Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::union_impl <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::union_impl Line | Count | Source | 436 | 1.46M | fn union_impl(&mut self, id1: Id, id2: Id) -> (Id, bool) { | 437 | 1.46M | fn concat<T>(to: &mut Vec<T>, mut from: Vec<T>) { | 438 | 1.46M | if to.len() < from.len() { | 439 | 1.46M | std::mem::swap(to, &mut from) | 440 | 1.46M | } | 441 | 1.46M | to.extend(from); | 442 | 1.46M | } | 443 | 1.46M | | 444 | 1.46M | let (to, from) = self.unionfind.union(id1, id2); | 445 | 0 | debug_assert_eq!(to, self.find(id1)); | 446 | 0 | debug_assert_eq!(to, self.find(id2)); | 447 | 1.46M | if to != from { | 448 | 561k | self.dirty_unions.push(to); | 449 | 561k | | 450 | 561k | // update the classes data structure | 451 | 561k | let from_class = self.classes[usize::from(from)].take().unwrap(); | 452 | 561k | let to_class = self.classes[usize::from(to)].as_mut().unwrap(); | 453 | 561k | | 454 | 561k | self.analysis.merge(&mut to_class.data, from_class.data); | 455 | 561k | concat(&mut to_class.nodes, from_class.nodes); | 456 | 561k | concat(&mut to_class.parents, from_class.parents); | 457 | 561k | | 458 | 561k | N::modify(self, to); | 459 | 900k | } | 460 | 1.46M | (to, to != from) | 461 | 1.46M | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::union_impl |
462 | | |
463 | | /// Unions two eclasses given their ids. |
464 | | /// |
465 | | /// The given ids need not be canonical. |
466 | | /// The returned `bool` indicates whether a union was done, |
467 | | /// so it's `false` if they were already equivalent. |
468 | | /// Both results are canonical. |
469 | 765k | pub fn union(&mut self, id1: Id, id2: Id) -> (Id, bool) { |
470 | 765k | let union = self.union_impl(id1, id2); |
471 | 765k | if union.1 && cfg!(feature = "upward-merging") { |
472 | 0 | self.process_unions(); |
473 | 765k | } |
474 | 765k | union |
475 | 765k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::union Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::union <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::union Line | Count | Source | 469 | 765k | pub fn union(&mut self, id1: Id, id2: Id) -> (Id, bool) { | 470 | 765k | let union = self.union_impl(id1, id2); | 471 | 765k | if union.1 && cfg!(feature = "upward-merging") { | 472 | 0 | self.process_unions(); | 473 | 765k | } | 474 | 765k | union | 475 | 765k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::union |
476 | | |
477 | | /// Returns a more debug-able representation of the egraph. |
478 | | /// |
479 | | /// [`EGraph`]s implement [`Debug`], but it ain't pretty. It |
480 | | /// prints a lot of stuff you probably don't care about. |
481 | | /// This method returns a wrapper that implements [`Debug`] in a |
482 | | /// slightly nicer way, just dumping enodes in each eclass. |
483 | | /// |
484 | | /// [`Debug`]: https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html |
485 | | /// [`EGraph`]: struct.EGraph.html |
486 | 0 | pub fn dump<'a>(&'a self) -> impl Debug + 'a { |
487 | 0 | EGraphDump(self) |
488 | 0 | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::dump Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::dump Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::dump Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::dump |
489 | | } |
490 | | |
491 | | // All the rebuilding stuff |
492 | | impl<L: Language, N: Analysis<L>> EGraph<L, N> { |
493 | | #[inline(never)] |
494 | 5.37k | fn rebuild_classes(&mut self) -> usize { |
495 | 5.37k | let mut classes_by_op = std::mem::take(&mut self.classes_by_op); |
496 | 9.55k | classes_by_op.values_mut().for_each(|ids| ids.clear()); Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild_classes::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild_classes::{closure#0}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild_classes::{closure#0}Line | Count | Source | 496 | 9.55k | classes_by_op.values_mut().for_each(|ids| ids.clear()); |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild_classes::{closure#0} |
497 | 5.37k | |
498 | 5.37k | let mut trimmed = 0; |
499 | 5.37k | |
500 | 5.37k | let uf = &self.unionfind; |
501 | 60.6k | for class in self.classes.iter_mut().filter_map(Option::as_mut) { |
502 | 60.6k | let old_len = class.len(); |
503 | 60.6k | class |
504 | 60.6k | .nodes |
505 | 60.6k | .iter_mut() |
506 | 1.08M | .for_each(|n| n.update_children(|id| uf.find(id))); Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild_classes::{closure#1}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild_classes::{closure#1}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild_classes::{closure#1}Line | Count | Source | 506 | 621k | .for_each(|n| n.update_children(|id| uf.find(id))); |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild_classes::{closure#1}Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild_classes::{closure#1}::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild_classes::{closure#1}::{closure#0}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild_classes::{closure#1}::{closure#0}Line | Count | Source | 506 | 1.08M | .for_each(|n| n.update_children(|id| uf.find(id))); |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild_classes::{closure#1}::{closure#0} |
507 | 60.6k | class.nodes.sort_unstable(); |
508 | 60.6k | class.nodes.dedup(); |
509 | 60.6k | |
510 | 60.6k | trimmed += old_len - class.nodes.len(); |
511 | 60.6k | |
512 | 60.6k | // TODO this is the slow version, could take advantage of sortedness |
513 | 60.6k | // maybe |
514 | 117k | let mut add = |n: &L| { |
515 | 117k | #[allow(clippy::mem_discriminant_non_enum)] |
516 | 117k | classes_by_op |
517 | 117k | .entry(std::mem::discriminant(&n)) |
518 | 117k | .or_default() |
519 | 117k | .insert(class.id) |
520 | 117k | }; Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild_classes::{closure#2}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild_classes::{closure#2}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild_classes::{closure#2}Line | Count | Source | 514 | 117k | let mut add = |n: &L| { | 515 | 117k | #[allow(clippy::mem_discriminant_non_enum)] | 516 | 117k | classes_by_op | 517 | 117k | .entry(std::mem::discriminant(&n)) | 518 | 117k | .or_default() | 519 | 117k | .insert(class.id) | 520 | 117k | }; |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild_classes::{closure#2} |
521 | | |
522 | | // we can go through the ops in order to dedup them, becaue we |
523 | | // just sorted them |
524 | 60.6k | let mut nodes = class.nodes.iter(); |
525 | 60.6k | if let Some(mut prev) = nodes.next() { |
526 | 60.6k | add(prev); |
527 | 144k | for n in nodes { |
528 | 83.9k | if !prev.matches(n) { |
529 | 56.7k | add(n); |
530 | 56.7k | prev = n; |
531 | 56.7k | } |
532 | | } |
533 | 0 | } |
534 | | } |
535 | | |
536 | | #[cfg(debug_assertions)] |
537 | | for ids in classes_by_op.values_mut() { |
538 | | let unique: indexmap::IndexSet<Id> = ids.iter().copied().collect(); |
539 | | assert_eq!(ids.len(), unique.len()); |
540 | | } |
541 | | |
542 | 5.37k | self.classes_by_op = classes_by_op; |
543 | 5.37k | trimmed |
544 | 5.37k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild_classes Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild_classes <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild_classes Line | Count | Source | 494 | 5.37k | fn rebuild_classes(&mut self) -> usize { | 495 | 5.37k | let mut classes_by_op = std::mem::take(&mut self.classes_by_op); | 496 | 5.37k | classes_by_op.values_mut().for_each(|ids| ids.clear()); | 497 | 5.37k | | 498 | 5.37k | let mut trimmed = 0; | 499 | 5.37k | | 500 | 5.37k | let uf = &self.unionfind; | 501 | 60.6k | for class in self.classes.iter_mut().filter_map(Option::as_mut) { | 502 | 60.6k | let old_len = class.len(); | 503 | 60.6k | class | 504 | 60.6k | .nodes | 505 | 60.6k | .iter_mut() | 506 | 60.6k | .for_each(|n| n.update_children(|id| uf.find(id))); | 507 | 60.6k | class.nodes.sort_unstable(); | 508 | 60.6k | class.nodes.dedup(); | 509 | 60.6k | | 510 | 60.6k | trimmed += old_len - class.nodes.len(); | 511 | 60.6k | | 512 | 60.6k | // TODO this is the slow version, could take advantage of sortedness | 513 | 60.6k | // maybe | 514 | 60.6k | let mut add = |n: &L| { | 515 | | #[allow(clippy::mem_discriminant_non_enum)] | 516 | | classes_by_op | 517 | | .entry(std::mem::discriminant(&n)) | 518 | | .or_default() | 519 | | .insert(class.id) | 520 | | }; | 521 | | | 522 | | // we can go through the ops in order to dedup them, becaue we | 523 | | // just sorted them | 524 | 60.6k | let mut nodes = class.nodes.iter(); | 525 | 60.6k | if let Some(mut prev) = nodes.next() { | 526 | 60.6k | add(prev); | 527 | 144k | for n in nodes { | 528 | 83.9k | if !prev.matches(n) { | 529 | 56.7k | add(n); | 530 | 56.7k | prev = n; | 531 | 56.7k | } | 532 | | } | 533 | 0 | } | 534 | | } | 535 | | | 536 | | #[cfg(debug_assertions)] | 537 | | for ids in classes_by_op.values_mut() { | 538 | | let unique: indexmap::IndexSet<Id> = ids.iter().copied().collect(); | 539 | | assert_eq!(ids.len(), unique.len()); | 540 | | } | 541 | | | 542 | 5.37k | self.classes_by_op = classes_by_op; | 543 | 5.37k | trimmed | 544 | 5.37k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild_classes |
545 | | |
546 | | #[inline(never)] |
547 | 0 | fn check_memo(&self) -> bool { |
548 | 0 | let mut test_memo = IndexMap::new(); |
549 | | |
550 | 0 | for (id, class) in self.classes.iter().enumerate() { |
551 | 0 | let id = Id::from(id); |
552 | 0 | let class = match class.as_ref() { |
553 | 0 | Some(class) => class, |
554 | 0 | None => continue, |
555 | | }; |
556 | 0 | assert_eq!(class.id, id); |
557 | 0 | for node in &class.nodes { |
558 | 0 | if let Some(old) = test_memo.insert(node, id) { |
559 | 0 | assert_eq!( |
560 | 0 | self.find(old), |
561 | 0 | self.find(id), |
562 | 0 | "Found unexpected equivalence for {:?}\n{:?}\nvs\n{:?}", |
563 | 0 | node, |
564 | 0 | self[self.find(id)].nodes, |
565 | 0 | self[self.find(old)].nodes, |
566 | | ); |
567 | 0 | } |
568 | | } |
569 | | } |
570 | | |
571 | 0 | for (n, e) in test_memo { |
572 | 0 | assert_eq!(e, self.find(e)); |
573 | 0 | assert_eq!( |
574 | 0 | Some(e), |
575 | 0 | self.memo.get(n).map(|id| self.find(*id)), Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::check_memo::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::check_memo::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::check_memo::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::check_memo::{closure#0} |
576 | 0 | "Entry for {:?} at {} in test_memo was incorrect", |
577 | | n, |
578 | | e |
579 | | ); |
580 | | } |
581 | | |
582 | 0 | true |
583 | 0 | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::check_memo Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::check_memo Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::check_memo Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::check_memo |
584 | | |
585 | | #[inline(never)] |
586 | 5.37k | fn process_unions(&mut self) { |
587 | 5.37k | let mut to_union = vec![]; |
588 | | |
589 | 8.14k | while !self.dirty_unions.is_empty() { |
590 | | // take the worklist, we'll get the stuff that's added the next time around |
591 | | // deduplicate the dirty list to avoid extra work |
592 | 2.77k | let mut todo = std::mem::take(&mut self.dirty_unions); |
593 | 561k | todo.iter_mut().for_each(|id| *id = self.find(*id)); Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::process_unions::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::process_unions::{closure#0}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::process_unions::{closure#0}Line | Count | Source | 593 | 561k | todo.iter_mut().for_each(|id| *id = self.find(*id)); |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::process_unions::{closure#0} |
594 | 2.77k | if cfg!(not(feature = "upward-merging")) { |
595 | 2.77k | todo.sort_unstable(); |
596 | 2.77k | todo.dedup(); |
597 | 2.77k | } |
598 | 2.77k | assert!(!todo.is_empty()); |
599 | | |
600 | 8.27k | for id in todo { |
601 | 5.49k | self.repairs_since_rebuild += 1; |
602 | 5.49k | let mut parents = std::mem::take(&mut self[id].parents); |
603 | | |
604 | 779k | for (n, _e) in &parents { |
605 | 774k | self.memo.remove(n); |
606 | 774k | } |
607 | | |
608 | 774k | parents.iter_mut().for_each(|(n, id)| { |
609 | 1.48M | n.update_children(|child| self.find(child)); Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::process_unions::{closure#1}::{closure#0}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::process_unions::{closure#1}::{closure#0}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::process_unions::{closure#1}::{closure#0}Line | Count | Source | 609 | 1.48M | n.update_children(|child| self.find(child)); |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::process_unions::{closure#1}::{closure#0} |
610 | 774k | *id = self.find(*id); |
611 | 774k | }); Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::process_unions::{closure#1}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::process_unions::{closure#1}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::process_unions::{closure#1}Line | Count | Source | 608 | 774k | parents.iter_mut().for_each(|(n, id)| { | 609 | 774k | n.update_children(|child| self.find(child)); | 610 | 774k | *id = self.find(*id); | 611 | 774k | }); |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::process_unions::{closure#1} |
612 | 5.49k | parents.sort_unstable(); |
613 | 5.49k | parents.dedup_by(|(n1, e1), (n2, e2)| { |
614 | 768k | n1 == n2 && { |
615 | 695k | to_union.push((*e1, *e2)); |
616 | 695k | true |
617 | | } |
618 | 768k | }); Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::process_unions::{closure#2}Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::process_unions::{closure#2}<egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::process_unions::{closure#2}Line | Count | Source | 614 | 768k | n1 == n2 && { | 615 | 695k | to_union.push((*e1, *e2)); | 616 | 695k | true | 617 | | } | 618 | 768k | }); |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::process_unions::{closure#2} |
619 | | |
620 | 83.6k | for (n, e) in &parents { |
621 | 78.1k | if let Some(old) = self.memo.insert(n.clone(), *e) { |
622 | 258 | to_union.push((old, *e)); |
623 | 77.9k | } |
624 | | } |
625 | | |
626 | 5.49k | self.propagate_metadata(&parents); |
627 | 5.49k | |
628 | 5.49k | self[id].parents = parents; |
629 | 5.49k | N::modify(self, id); |
630 | | } |
631 | | |
632 | 696k | for (id1, id2) in to_union.drain(..) { |
633 | 696k | let (to, did_something) = self.union_impl(id1, id2); |
634 | 696k | if did_something { |
635 | 608 | self.dirty_unions.push(to); |
636 | 695k | } |
637 | | } |
638 | | } |
639 | | |
640 | 5.37k | assert!(self.dirty_unions.is_empty()); |
641 | 5.37k | assert!(to_union.is_empty()); |
642 | 5.37k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::process_unions Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::process_unions <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::process_unions Line | Count | Source | 586 | 5.37k | fn process_unions(&mut self) { | 587 | 5.37k | let mut to_union = vec![]; | 588 | | | 589 | 8.14k | while !self.dirty_unions.is_empty() { | 590 | | // take the worklist, we'll get the stuff that's added the next time around | 591 | | // deduplicate the dirty list to avoid extra work | 592 | 2.77k | let mut todo = std::mem::take(&mut self.dirty_unions); | 593 | 2.77k | todo.iter_mut().for_each(|id| *id = self.find(*id)); | 594 | 2.77k | if cfg!(not(feature = "upward-merging")) { | 595 | 2.77k | todo.sort_unstable(); | 596 | 2.77k | todo.dedup(); | 597 | 2.77k | } | 598 | 2.77k | assert!(!todo.is_empty()); | 599 | | | 600 | 8.27k | for id in todo { | 601 | 5.49k | self.repairs_since_rebuild += 1; | 602 | 5.49k | let mut parents = std::mem::take(&mut self[id].parents); | 603 | | | 604 | 779k | for (n, _e) in &parents { | 605 | 774k | self.memo.remove(n); | 606 | 774k | } | 607 | | | 608 | 5.49k | parents.iter_mut().for_each(|(n, id)| { | 609 | | n.update_children(|child| self.find(child)); | 610 | | *id = self.find(*id); | 611 | 5.49k | }); | 612 | 5.49k | parents.sort_unstable(); | 613 | 5.49k | parents.dedup_by(|(n1, e1), (n2, e2)| { | 614 | | n1 == n2 && { | 615 | | to_union.push((*e1, *e2)); | 616 | | true | 617 | | } | 618 | 5.49k | }); | 619 | | | 620 | 83.6k | for (n, e) in &parents { | 621 | 78.1k | if let Some(old) = self.memo.insert(n.clone(), *e) { | 622 | 258 | to_union.push((old, *e)); | 623 | 77.9k | } | 624 | | } | 625 | | | 626 | 5.49k | self.propagate_metadata(&parents); | 627 | 5.49k | | 628 | 5.49k | self[id].parents = parents; | 629 | 5.49k | N::modify(self, id); | 630 | | } | 631 | | | 632 | 696k | for (id1, id2) in to_union.drain(..) { | 633 | 696k | let (to, did_something) = self.union_impl(id1, id2); | 634 | 696k | if did_something { | 635 | 608 | self.dirty_unions.push(to); | 636 | 695k | } | 637 | | } | 638 | | } | 639 | | | 640 | 5.37k | assert!(self.dirty_unions.is_empty()); | 641 | 5.37k | assert!(to_union.is_empty()); | 642 | 5.37k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::process_unions |
643 | | |
644 | | /// Restores the egraph invariants of congruence and enode uniqueness. |
645 | | /// |
646 | | /// As mentioned [above](struct.EGraph.html#invariants-and-rebuilding), |
647 | | /// `egg` takes a lazy approach to maintaining the egraph invariants. |
648 | | /// The `rebuild` method allows the user to manually restore those |
649 | | /// invariants at a time of their choosing. It's a reasonably |
650 | | /// fast, linear-ish traversal through the egraph. |
651 | | /// |
652 | | /// # Example |
653 | | /// ``` |
654 | | /// use egg::{*, SymbolLang as S}; |
655 | | /// let mut egraph = EGraph::<S, ()>::default(); |
656 | | /// let x = egraph.add(S::leaf("x")); |
657 | | /// let y = egraph.add(S::leaf("y")); |
658 | | /// let ax = egraph.add_expr(&"(+ a x)".parse().unwrap()); |
659 | | /// let ay = egraph.add_expr(&"(+ a y)".parse().unwrap()); |
660 | | /// |
661 | | /// // The effects of this union aren't yet visible; ax and ay |
662 | | /// // should be equivalent by congruence since x = y. |
663 | | /// egraph.union(x, y); |
664 | | /// // Classes: [x y] [ax] [ay] [a] |
665 | | /// # #[cfg(not(feature = "upward-merging"))] |
666 | | /// assert_eq!(egraph.number_of_classes(), 4); |
667 | | /// # #[cfg(not(feature = "upward-merging"))] |
668 | | /// assert_ne!(egraph.find(ax), egraph.find(ay)); |
669 | | /// |
670 | | /// // Rebuilding restores the invariants, finding the "missing" equivalence |
671 | | /// egraph.rebuild(); |
672 | | /// // Classes: [x y] [ax ay] [a] |
673 | | /// assert_eq!(egraph.number_of_classes(), 3); |
674 | | /// assert_eq!(egraph.find(ax), egraph.find(ay)); |
675 | | /// ``` |
676 | 5.37k | pub fn rebuild(&mut self) -> usize { |
677 | 5.37k | let old_hc_size = self.memo.len(); |
678 | 5.37k | let old_n_eclasses = self.number_of_classes(); |
679 | 5.37k | |
680 | 5.37k | let start = instant::Instant::now(); |
681 | 5.37k | |
682 | 5.37k | self.process_unions(); |
683 | 5.37k | let n_unions = std::mem::take(&mut self.repairs_since_rebuild); |
684 | 5.37k | let trimmed_nodes = self.rebuild_classes(); |
685 | 5.37k | |
686 | 5.37k | let elapsed = start.elapsed(); |
687 | 5.37k | info!( |
688 | 0 | concat!( |
689 | 0 | "REBUILT! in {}.{:03}s\n", |
690 | 0 | " Old: hc size {}, eclasses: {}\n", |
691 | 0 | " New: hc size {}, eclasses: {}\n", |
692 | 0 | " unions: {}, trimmed nodes: {}" |
693 | 0 | ), |
694 | 0 | elapsed.as_secs(), |
695 | 0 | elapsed.subsec_millis(), |
696 | 0 | old_hc_size, |
697 | 0 | old_n_eclasses, |
698 | 0 | self.memo.len(), |
699 | 0 | self.number_of_classes(), |
700 | | n_unions, |
701 | | trimmed_nodes, |
702 | | ); |
703 | | |
704 | 0 | debug_assert!(self.check_memo()); |
705 | 5.37k | n_unions |
706 | 5.37k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::rebuild Line | Count | Source | 676 | 5.37k | pub fn rebuild(&mut self) -> usize { | 677 | 5.37k | let old_hc_size = self.memo.len(); | 678 | 5.37k | let old_n_eclasses = self.number_of_classes(); | 679 | 5.37k | | 680 | 5.37k | let start = instant::Instant::now(); | 681 | 5.37k | | 682 | 5.37k | self.process_unions(); | 683 | 5.37k | let n_unions = std::mem::take(&mut self.repairs_since_rebuild); | 684 | 5.37k | let trimmed_nodes = self.rebuild_classes(); | 685 | 5.37k | | 686 | 5.37k | let elapsed = start.elapsed(); | 687 | 5.37k | info!( | 688 | 0 | concat!( | 689 | 0 | "REBUILT! in {}.{:03}s\n", | 690 | 0 | " Old: hc size {}, eclasses: {}\n", | 691 | 0 | " New: hc size {}, eclasses: {}\n", | 692 | 0 | " unions: {}, trimmed nodes: {}" | 693 | 0 | ), | 694 | 0 | elapsed.as_secs(), | 695 | 0 | elapsed.subsec_millis(), | 696 | 0 | old_hc_size, | 697 | 0 | old_n_eclasses, | 698 | 0 | self.memo.len(), | 699 | 0 | self.number_of_classes(), | 700 | | n_unions, | 701 | | trimmed_nodes, | 702 | | ); | 703 | | | 704 | 0 | debug_assert!(self.check_memo()); | 705 | 5.37k | n_unions | 706 | 5.37k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::rebuild |
707 | | |
708 | | #[inline(never)] |
709 | 5.49k | fn propagate_metadata(&mut self, parents: &[(L, Id)]) { |
710 | 83.6k | for (n, e) in parents { |
711 | 78.1k | let e = self.find(*e); |
712 | 78.1k | let node_data = N::make(self, n); |
713 | 78.1k | let class = self.classes[usize::from(e)].as_mut().unwrap(); |
714 | 78.1k | if self.analysis.merge(&mut class.data, node_data) { |
715 | | // self.dirty_unions.push(e); // NOTE: i dont think this is necessary |
716 | 0 | let e_parents = std::mem::take(&mut class.parents); |
717 | 0 | self.propagate_metadata(&e_parents); |
718 | 0 | self[e].parents = e_parents; |
719 | 0 | N::modify(self, e) |
720 | 78.1k | } |
721 | | } |
722 | 5.49k | } Unexecuted instantiation: <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::propagate_metadata Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::propagate_metadata <egg::egraph::EGraph<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis>>::propagate_metadata Line | Count | Source | 709 | 5.49k | fn propagate_metadata(&mut self, parents: &[(L, Id)]) { | 710 | 83.6k | for (n, e) in parents { | 711 | 78.1k | let e = self.find(*e); | 712 | 78.1k | let node_data = N::make(self, n); | 713 | 78.1k | let class = self.classes[usize::from(e)].as_mut().unwrap(); | 714 | 78.1k | if self.analysis.merge(&mut class.data, node_data) { | 715 | | // self.dirty_unions.push(e); // NOTE: i dont think this is necessary | 716 | 0 | let e_parents = std::mem::take(&mut class.parents); | 717 | 0 | self.propagate_metadata(&e_parents); | 718 | 0 | self[e].parents = e_parents; | 719 | 0 | N::modify(self, e) | 720 | 78.1k | } | 721 | | } | 722 | 5.49k | } |
Unexecuted instantiation: <egg::egraph::EGraph<_, _>>::propagate_metadata |
723 | | } |
724 | | |
725 | | struct EGraphDump<'a, L: Language, N: Analysis<L>>(&'a EGraph<L, N>); |
726 | | |
727 | | impl<'a, L: Language, N: Analysis<L>> Debug for EGraphDump<'a, L, N> { |
728 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
729 | 0 | let mut ids: Vec<Id> = self.0.classes().map(|c| c.id).collect(); Unexecuted instantiation: <egg::egraph::EGraphDump<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::fmt::Debug>::fmt::{closure#0}Unexecuted instantiation: <egg::egraph::EGraphDump<_, _> as core::fmt::Debug>::fmt::{closure#0}Unexecuted instantiation: <egg::egraph::EGraphDump<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::fmt::Debug>::fmt::{closure#0}Unexecuted instantiation: <egg::egraph::EGraphDump<_, _> as core::fmt::Debug>::fmt::{closure#0} |
730 | 0 | ids.sort(); |
731 | 0 | for id in ids { |
732 | 0 | let mut nodes = self.0[id].nodes.clone(); |
733 | 0 | nodes.sort(); |
734 | 0 | writeln!(f, "{}: {:?}", id, nodes)? |
735 | | } |
736 | 0 | Ok(()) |
737 | 0 | } Unexecuted instantiation: <egg::egraph::EGraphDump<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::fmt::Debug>::fmt Unexecuted instantiation: <egg::egraph::EGraphDump<_, _> as core::fmt::Debug>::fmt Unexecuted instantiation: <egg::egraph::EGraphDump<wasm_mutate::mutators::peephole::eggsy::lang::Lang, wasm_mutate::mutators::peephole::eggsy::analysis::PeepholeMutationAnalysis> as core::fmt::Debug>::fmt Unexecuted instantiation: <egg::egraph::EGraphDump<_, _> as core::fmt::Debug>::fmt |
738 | | } |
739 | | |
740 | | #[cfg(test)] |
741 | | mod tests { |
742 | | |
743 | | use crate::*; |
744 | | |
745 | | #[test] |
746 | | fn simple_add() { |
747 | | use SymbolLang as S; |
748 | | |
749 | | crate::init_logger(); |
750 | | let mut egraph = EGraph::<S, ()>::default(); |
751 | | |
752 | | let x = egraph.add(S::leaf("x")); |
753 | | let x2 = egraph.add(S::leaf("x")); |
754 | | let _plus = egraph.add(S::new("+", vec![x, x2])); |
755 | | |
756 | | let y = egraph.add(S::leaf("y")); |
757 | | |
758 | | egraph.union(x, y); |
759 | | egraph.rebuild(); |
760 | | |
761 | | egraph.dot().to_dot("target/foo.dot").unwrap(); |
762 | | |
763 | | assert_eq!(2 + 2, 4); |
764 | | } |
765 | | } |