]>
Commit | Line | Data |
---|---|---|
ba9703b0 XL |
1 | // Copyright 2017 The Rust Project Developers. See the COPYRIGHT\r |
2 | // file at the top-level directory of this distribution and at\r | |
3 | // http://rust-lang.org/COPYRIGHT.\r | |
4 | //\r | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or\r | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license\r | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your\r | |
8 | // option. This file may not be copied, modified, or distributed\r | |
9 | // except according to those terms.\r | |
10 | \r | |
11 | use datafrog::{Iteration, Relation, RelationLeaper};\r | |
12 | use std::time::Instant;\r | |
13 | \r | |
14 | use crate::facts::FactTypes;\r | |
15 | use crate::output::{Context, Output};\r | |
16 | \r | |
17 | pub(super) fn compute<T: FactTypes>(\r | |
18 | ctx: &Context<'_, T>,\r | |
19 | result: &mut Output<T>,\r | |
20 | ) -> Relation<(T::Loan, T::Point)> {\r | |
21 | let timer = Instant::now();\r | |
22 | \r | |
23 | let errors = {\r | |
24 | // Static inputs\r | |
25 | let origin_live_on_entry_rel = &ctx.origin_live_on_entry;\r | |
26 | let cfg_edge_rel = &ctx.cfg_edge;\r | |
27 | let killed_rel = &ctx.killed;\r | |
28 | \r | |
29 | // Create a new iteration context, ...\r | |
30 | let mut iteration = Iteration::new();\r | |
31 | \r | |
32 | // `invalidates` facts, stored ready for joins\r | |
33 | let invalidates = iteration.variable::<((T::Loan, T::Point), ())>("invalidates");\r | |
34 | \r | |
35 | // we need `origin_live_on_entry` in both variable and relation forms,\r | |
36 | // (respectively, for join and antijoin).\r | |
37 | let origin_live_on_entry_var =\r | |
38 | iteration.variable::<((T::Origin, T::Point), ())>("origin_live_on_entry");\r | |
39 | \r | |
40 | // `borrow_region` input but organized for join\r | |
41 | let borrow_region_op =\r | |
42 | iteration.variable::<((T::Origin, T::Point), T::Loan)>("borrow_region_op");\r | |
43 | \r | |
44 | // .decl subset(origin1, origin2, point)\r | |
45 | //\r | |
46 | // Indicates that `origin1: origin2` at `point`.\r | |
47 | let subset_o1p = iteration.variable::<((T::Origin, T::Point), T::Origin)>("subset_o1p");\r | |
48 | \r | |
49 | // .decl requires(origin, loan, point)\r | |
50 | //\r | |
51 | // At `point`, things with `origin` may depend on data from `loan`.\r | |
52 | let requires_op = iteration.variable::<((T::Origin, T::Point), T::Loan)>("requires_op");\r | |
53 | \r | |
54 | // .decl borrow_live_at(loan, point)\r | |
55 | //\r | |
56 | // True if the restrictions of the `loan` need to be enforced at `point`.\r | |
57 | let borrow_live_at = iteration.variable::<((T::Loan, T::Point), ())>("borrow_live_at");\r | |
58 | \r | |
59 | // .decl live_to_dying_regions(origin1, origin2, point1, point2)\r | |
60 | //\r | |
61 | // The origins `origin1` and `origin2` are "live to dead"\r | |
62 | // on the edge `point1 -> point2` if:\r | |
63 | //\r | |
64 | // - In `point1`, `origin1` <= `origin2`\r | |
65 | // - In `point2`, `origin1` is live but `origin2` is dead.\r | |
66 | //\r | |
67 | // In that case, `point2` would like to add all the\r | |
68 | // live things reachable from `origin2` to `origin1`.\r | |
69 | //\r | |
70 | let live_to_dying_regions_o2pq = iteration\r | |
71 | .variable::<((T::Origin, T::Point, T::Point), T::Origin)>("live_to_dying_regions_o2pq");\r | |
72 | \r | |
73 | // .decl dying_region_requires((origin, point1, point2), loan)\r | |
74 | //\r | |
75 | // The `origin` requires `loan`, but the `origin` goes dead\r | |
76 | // along the edge `point1 -> point2`.\r | |
77 | let dying_region_requires = iteration\r | |
78 | .variable::<((T::Origin, T::Point, T::Point), T::Loan)>("dying_region_requires");\r | |
79 | \r | |
80 | // .decl dying_can_reach_origins(origin, point1, point2)\r | |
81 | //\r | |
82 | // Contains dead origins where we are interested\r | |
83 | // in computing the transitive closure of things they\r | |
84 | // can reach.\r | |
85 | //\r | |
86 | // FIXME: this relation was named before renaming the `regions` atoms to `origins`, and\r | |
87 | // will need to be renamed to change "_origins" to "_ascendants", "_roots", etc.\r | |
88 | let dying_can_reach_origins =\r | |
89 | iteration.variable::<((T::Origin, T::Point), T::Point)>("dying_can_reach_origins");\r | |
90 | \r | |
91 | // .decl dying_can_reach(origin1, origin2, point1, point2)\r | |
92 | //\r | |
93 | // Indicates that `origin1`, which is dead\r | |
94 | // in `point2`, can reach `origin2` in `point1`.\r | |
95 | //\r | |
96 | // This is effectively the transitive subset\r | |
97 | // relation, but we try to limit it to origins\r | |
98 | // that are dying on the edge `point1 -> point2`.\r | |
99 | let dying_can_reach_o2q =\r | |
100 | iteration.variable::<((T::Origin, T::Point), (T::Origin, T::Point))>("dying_can_reach");\r | |
101 | let dying_can_reach_1 = iteration.variable_indistinct("dying_can_reach_1");\r | |
102 | \r | |
103 | // .decl dying_can_reach_live(origin1, origin2, point1, point2)\r | |
104 | //\r | |
105 | // Indicates that, along the edge `point1 -> point2`, the dead (in `point2`)\r | |
106 | // `origin1` can reach the live (in `point2`) `origin2` via a subset\r | |
107 | // relation. This is a subset of the full `dying_can_reach`\r | |
108 | // relation where we filter down to those cases where `origin2` is\r | |
109 | // live in `point2`.\r | |
110 | let dying_can_reach_live = iteration\r | |
111 | .variable::<((T::Origin, T::Point, T::Point), T::Origin)>("dying_can_reach_live");\r | |
112 | \r | |
113 | // .decl dead_borrow_region_can_reach_root((origin, point), loan)\r | |
114 | //\r | |
115 | // Indicates a "borrow region" `origin` at `point` which is not live on\r | |
116 | // entry to `point`.\r | |
117 | let dead_borrow_region_can_reach_root = iteration\r | |
118 | .variable::<((T::Origin, T::Point), T::Loan)>("dead_borrow_region_can_reach_root");\r | |
119 | \r | |
120 | // .decl dead_borrow_region_can_reach_dead((origin2, point), loan)\r | |
121 | let dead_borrow_region_can_reach_dead = iteration\r | |
122 | .variable::<((T::Origin, T::Point), T::Loan)>("dead_borrow_region_can_reach_dead");\r | |
123 | let dead_borrow_region_can_reach_dead_1 =\r | |
124 | iteration.variable_indistinct("dead_borrow_region_can_reach_dead_1");\r | |
125 | \r | |
126 | // .decl errors(loan, point)\r | |
127 | let errors = iteration.variable("errors");\r | |
128 | \r | |
129 | // Make "variable" versions of the relations, needed for joins.\r | |
130 | borrow_region_op.extend(\r | |
131 | ctx.borrow_region\r | |
132 | .iter()\r | |
133 | .map(|&(origin, loan, point)| ((origin, point), loan)),\r | |
134 | );\r | |
135 | invalidates.extend(\r | |
136 | ctx.invalidates\r | |
137 | .iter()\r | |
138 | .map(|&(loan, point)| ((loan, point), ())),\r | |
139 | );\r | |
140 | origin_live_on_entry_var.extend(\r | |
141 | origin_live_on_entry_rel\r | |
142 | .iter()\r | |
143 | .map(|&(origin, point)| ((origin, point), ())),\r | |
144 | );\r | |
145 | \r | |
146 | // subset(origin1, origin2, point) :- outlives(origin1, origin2, point).\r | |
147 | subset_o1p.extend(\r | |
148 | ctx.outlives\r | |
149 | .iter()\r | |
150 | .map(|&(origin1, origin2, point)| ((origin1, point), origin2)),\r | |
151 | );\r | |
152 | \r | |
153 | // requires(origin, loan, point) :- borrow_region(origin, loan, point).\r | |
154 | requires_op.extend(\r | |
155 | ctx.borrow_region\r | |
156 | .iter()\r | |
157 | .map(|&(origin, loan, point)| ((origin, point), loan)),\r | |
158 | );\r | |
159 | \r | |
160 | // .. and then start iterating rules!\r | |
161 | while iteration.changed() {\r | |
162 | // Cleanup step: remove symmetries\r | |
163 | // - remove origins which are `subset`s of themselves\r | |
164 | //\r | |
165 | // FIXME: investigate whether is there a better way to do that without complicating\r | |
166 | // the rules too much, because it would also require temporary variables and\r | |
167 | // impact performance. Until then, the big reduction in tuples improves performance\r | |
168 | // a lot, even if we're potentially adding a small number of tuples\r | |
169 | // per round just to remove them in the next round.\r | |
170 | subset_o1p\r | |
171 | .recent\r | |
172 | .borrow_mut()\r | |
173 | .elements\r | |
174 | .retain(|&((origin1, _), origin2)| origin1 != origin2);\r | |
175 | \r | |
176 | // live_to_dying_regions(origin1, origin2, point1, point2) :-\r | |
177 | // subset(origin1, origin2, point1),\r | |
178 | // cfg_edge(point1, point2),\r | |
179 | // origin_live_on_entry(origin1, point2),\r | |
180 | // !origin_live_on_entry(origin2, point2).\r | |
181 | live_to_dying_regions_o2pq.from_leapjoin(\r | |
182 | &subset_o1p,\r | |
183 | (\r | |
184 | cfg_edge_rel.extend_with(|&((_, point1), _)| point1),\r | |
185 | origin_live_on_entry_rel.extend_with(|&((origin1, _), _)| origin1),\r | |
186 | origin_live_on_entry_rel.extend_anti(|&((_, _), origin2)| origin2),\r | |
187 | ),\r | |
188 | |&((origin1, point1), origin2), &point2| ((origin2, point1, point2), origin1),\r | |
189 | );\r | |
190 | \r | |
191 | // dying_region_requires((origin, point1, point2), loan) :-\r | |
192 | // requires(origin, loan, point1),\r | |
193 | // !killed(loan, point1),\r | |
194 | // cfg_edge(point1, point2),\r | |
195 | // !origin_live_on_entry(origin, point2).\r | |
196 | dying_region_requires.from_leapjoin(\r | |
197 | &requires_op,\r | |
198 | (\r | |
199 | killed_rel.filter_anti(|&((_, point1), loan)| (loan, point1)),\r | |
200 | cfg_edge_rel.extend_with(|&((_, point1), _)| point1),\r | |
201 | origin_live_on_entry_rel.extend_anti(|&((origin, _), _)| origin),\r | |
202 | ),\r | |
203 | |&((origin, point1), loan), &point2| ((origin, point1, point2), loan),\r | |
204 | );\r | |
205 | \r | |
206 | // dying_can_reach_origins(origin2, point1, point2) :-\r | |
207 | // live_to_dying_regions(_, origin2, point1, point2).\r | |
208 | dying_can_reach_origins.from_map(\r | |
209 | &live_to_dying_regions_o2pq,\r | |
210 | |&((origin2, point1, point2), _origin1)| ((origin2, point1), point2),\r | |
211 | );\r | |
212 | \r | |
213 | // dying_can_reach_origins(origin, point1, point2) :-\r | |
214 | // dying_region_requires(origin, point1, point2, _loan).\r | |
215 | dying_can_reach_origins.from_map(\r | |
216 | &dying_region_requires,\r | |
217 | |&((origin, point1, point2), _loan)| ((origin, point1), point2),\r | |
218 | );\r | |
219 | \r | |
220 | // dying_can_reach(origin1, origin2, point1, point2) :-\r | |
221 | // dying_can_reach_origins(origin1, point1, point2),\r | |
222 | // subset(origin1, origin2, point1).\r | |
223 | dying_can_reach_o2q.from_join(\r | |
224 | &dying_can_reach_origins,\r | |
225 | &subset_o1p,\r | |
226 | |&(origin1, point1), &point2, &origin2| ((origin2, point2), (origin1, point1)),\r | |
227 | );\r | |
228 | \r | |
229 | // dying_can_reach(origin1, origin3, point1, point2) :-\r | |
230 | // dying_can_reach(origin1, origin2, point1, point2),\r | |
231 | // !origin_live_on_entry(origin2, point2),\r | |
232 | // subset(origin2, origin3, point1).\r | |
233 | //\r | |
234 | // This is the "transitive closure" rule, but\r | |
235 | // note that we only apply it with the\r | |
236 | // "intermediate" `origin2` is dead at `point2`.\r | |
237 | dying_can_reach_1.from_antijoin(\r | |
238 | &dying_can_reach_o2q,\r | |
239 | &origin_live_on_entry_rel,\r | |
240 | |&(origin2, point2), &(origin1, point1)| ((origin2, point1), (origin1, point2)),\r | |
241 | );\r | |
242 | dying_can_reach_o2q.from_join(\r | |
243 | &dying_can_reach_1,\r | |
244 | &subset_o1p,\r | |
245 | |&(_origin2, point1), &(origin1, point2), &origin3| {\r | |
246 | ((origin3, point2), (origin1, point1))\r | |
247 | },\r | |
248 | );\r | |
249 | \r | |
250 | // dying_can_reach_live(origin1, origin2, point1, point2) :-\r | |
251 | // dying_can_reach(origin1, origin2, point1, point2),\r | |
252 | // origin_live_on_entry(origin2, point2).\r | |
253 | dying_can_reach_live.from_join(\r | |
254 | &dying_can_reach_o2q,\r | |
255 | &origin_live_on_entry_var,\r | |
256 | |&(origin2, point2), &(origin1, point1), _| ((origin1, point1, point2), origin2),\r | |
257 | );\r | |
258 | \r | |
259 | // subset(origin1, origin2, point2) :-\r | |
260 | // subset(origin1, origin2, point1),\r | |
261 | // cfg_edge(point1, point2),\r | |
262 | // origin_live_on_entry(origin1, point2),\r | |
263 | // origin_live_on_entry(origin2, point2).\r | |
264 | //\r | |
265 | // Carry `origin1 <= origin2` from `point1` into `point2` if both `origin1` and\r | |
266 | // `origin2` are live in `point2`.\r | |
267 | subset_o1p.from_leapjoin(\r | |
268 | &subset_o1p,\r | |
269 | (\r | |
270 | cfg_edge_rel.extend_with(|&((_, point1), _)| point1),\r | |
271 | origin_live_on_entry_rel.extend_with(|&((origin1, _), _)| origin1),\r | |
272 | origin_live_on_entry_rel.extend_with(|&((_, _), origin2)| origin2),\r | |
273 | ),\r | |
274 | |&((origin1, _point1), origin2), &point2| ((origin1, point2), origin2),\r | |
275 | );\r | |
276 | \r | |
277 | // subset(origin1, origin3, point2) :-\r | |
278 | // live_to_dying_regions(origin1, origin2, point1, point2),\r | |
279 | // dying_can_reach_live(origin2, origin3, point1, point2).\r | |
280 | subset_o1p.from_join(\r | |
281 | &live_to_dying_regions_o2pq,\r | |
282 | &dying_can_reach_live,\r | |
283 | |&(_origin2, _point1, point2), &origin1, &origin3| ((origin1, point2), origin3),\r | |
284 | );\r | |
285 | \r | |
286 | // requires(origin2, loan, point2) :-\r | |
287 | // dying_region_requires(origin1, loan, point1, point2),\r | |
288 | // dying_can_reach_live(origin1, origin2, point1, point2).\r | |
289 | //\r | |
290 | // Communicate a `origin1 requires loan` relation across\r | |
291 | // an edge `point1 -> point2` where `origin1` is dead in `point2`; in\r | |
292 | // that case, for each origin `origin2` live in `point2`\r | |
293 | // where `origin1 <= origin2` in `point1`, we add `origin2 requires loan`\r | |
294 | // to `point2`.\r | |
295 | requires_op.from_join(\r | |
296 | &dying_region_requires,\r | |
297 | &dying_can_reach_live,\r | |
298 | |&(_origin1, _point1, point2), &loan, &origin2| ((origin2, point2), loan),\r | |
299 | );\r | |
300 | \r | |
301 | // requires(origin, loan, point2) :-\r | |
302 | // requires(origin, loan, point1),\r | |
303 | // !killed(loan, point1),\r | |
304 | // cfg_edge(point1, point2),\r | |
305 | // origin_live_on_entry(origin, point2).\r | |
306 | requires_op.from_leapjoin(\r | |
307 | &requires_op,\r | |
308 | (\r | |
309 | killed_rel.filter_anti(|&((_, point1), loan)| (loan, point1)),\r | |
310 | cfg_edge_rel.extend_with(|&((_, point1), _)| point1),\r | |
311 | origin_live_on_entry_rel.extend_with(|&((origin, _), _)| origin),\r | |
312 | ),\r | |
313 | |&((origin, _), loan), &point2| ((origin, point2), loan),\r | |
314 | );\r | |
315 | \r | |
316 | // dead_borrow_region_can_reach_root((origin, point), loan) :-\r | |
317 | // borrow_region(origin, loan, point),\r | |
318 | // !origin_live_on_entry(origin, point).\r | |
319 | dead_borrow_region_can_reach_root.from_antijoin(\r | |
320 | &borrow_region_op,\r | |
321 | &origin_live_on_entry_rel,\r | |
322 | |&(origin, point), &loan| ((origin, point), loan),\r | |
323 | );\r | |
324 | \r | |
325 | // dead_borrow_region_can_reach_dead((origin, point), loan) :-\r | |
326 | // dead_borrow_region_can_reach_root((origin, point), loan).\r | |
327 | dead_borrow_region_can_reach_dead\r | |
328 | .from_map(&dead_borrow_region_can_reach_root, |&tuple| tuple);\r | |
329 | \r | |
330 | // dead_borrow_region_can_reach_dead((origin2, point), loan) :-\r | |
331 | // dead_borrow_region_can_reach_dead(origin1, loan, point),\r | |
332 | // subset(origin1, origin2, point),\r | |
333 | // !origin_live_on_entry(origin2, point).\r | |
334 | dead_borrow_region_can_reach_dead_1.from_join(\r | |
335 | &dead_borrow_region_can_reach_dead,\r | |
336 | &subset_o1p,\r | |
337 | |&(_origin1, point), &loan, &origin2| ((origin2, point), loan),\r | |
338 | );\r | |
339 | dead_borrow_region_can_reach_dead.from_antijoin(\r | |
340 | &dead_borrow_region_can_reach_dead_1,\r | |
341 | &origin_live_on_entry_rel,\r | |
342 | |&(origin2, point), &loan| ((origin2, point), loan),\r | |
343 | );\r | |
344 | \r | |
345 | // borrow_live_at(loan, point) :-\r | |
346 | // requires(origin, loan, point),\r | |
347 | // origin_live_on_entry(origin, point).\r | |
348 | borrow_live_at.from_join(\r | |
349 | &requires_op,\r | |
350 | &origin_live_on_entry_var,\r | |
351 | |&(_origin, point), &loan, _| ((loan, point), ()),\r | |
352 | );\r | |
353 | \r | |
354 | // borrow_live_at(loan, point) :-\r | |
355 | // dead_borrow_region_can_reach_dead(origin1, loan, point),\r | |
356 | // subset(origin1, origin2, point),\r | |
357 | // origin_live_on_entry(origin2, point).\r | |
358 | //\r | |
359 | // NB: the datafrog code below uses\r | |
360 | // `dead_borrow_region_can_reach_dead_1`, which is equal\r | |
361 | // to `dead_borrow_region_can_reach_dead` and `subset`\r | |
362 | // joined together.\r | |
363 | borrow_live_at.from_join(\r | |
364 | &dead_borrow_region_can_reach_dead_1,\r | |
365 | &origin_live_on_entry_var,\r | |
366 | |&(_origin2, point), &loan, _| ((loan, point), ()),\r | |
367 | );\r | |
368 | \r | |
369 | // errors(loan, point) :-\r | |
370 | // invalidates(loan, point),\r | |
371 | // borrow_live_at(loan, point).\r | |
372 | errors.from_join(&invalidates, &borrow_live_at, |&(loan, point), _, _| {\r | |
373 | (loan, point)\r | |
374 | });\r | |
375 | }\r | |
376 | \r | |
377 | if result.dump_enabled {\r | |
378 | let subset_o1p = subset_o1p.complete();\r | |
379 | assert!(\r | |
380 | subset_o1p\r | |
381 | .iter()\r | |
382 | .filter(|&((origin1, _), origin2)| origin1 == origin2)\r | |
383 | .count()\r | |
384 | == 0,\r | |
385 | "unwanted subset symmetries"\r | |
386 | );\r | |
387 | for &((origin1, location), origin2) in subset_o1p.iter() {\r | |
388 | result\r | |
389 | .subset\r | |
390 | .entry(location)\r | |
391 | .or_default()\r | |
392 | .entry(origin1)\r | |
393 | .or_default()\r | |
394 | .insert(origin2);\r | |
395 | }\r | |
396 | \r | |
397 | let requires_op = requires_op.complete();\r | |
398 | for &((origin, location), loan) in requires_op.iter() {\r | |
399 | result\r | |
400 | .restricts\r | |
401 | .entry(location)\r | |
402 | .or_default()\r | |
403 | .entry(origin)\r | |
404 | .or_default()\r | |
405 | .insert(loan);\r | |
406 | }\r | |
407 | \r | |
408 | let borrow_live_at = borrow_live_at.complete();\r | |
409 | for &((loan, location), _) in borrow_live_at.iter() {\r | |
410 | result\r | |
411 | .borrow_live_at\r | |
412 | .entry(location)\r | |
413 | .or_default()\r | |
414 | .push(loan);\r | |
415 | }\r | |
416 | }\r | |
417 | \r | |
418 | errors.complete()\r | |
419 | };\r | |
420 | \r | |
421 | info!(\r | |
422 | "errors is complete: {} tuples, {:?}",\r | |
423 | errors.len(),\r | |
424 | timer.elapsed()\r | |
425 | );\r | |
426 | \r | |
427 | errors\r | |
428 | }\r |