1use std::collections::{BTreeMap, BTreeSet};
12
13use crate::memory::MemoryEffect;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16struct AccessHistory<I> {
17 last_writer: Option<I>,
18 readers_since_write: Vec<I>,
19}
20
21impl<I> Default for AccessHistory<I> {
22 fn default() -> Self {
23 Self {
24 last_writer: None,
25 readers_since_write: Vec::new(),
26 }
27 }
28}
29
30impl<I: Copy + Eq> AccessHistory<I> {
31 fn record_reader(&mut self, instruction: I) {
32 if self.readers_since_write.last() != Some(&instruction) {
33 self.readers_since_write.push(instruction);
34 }
35 }
36}
37
38#[derive(Debug, Clone)]
39struct MemorySegment<I> {
40 end: i64,
41 history: AccessHistory<I>,
42}
43
44#[derive(Debug)]
45struct RangeMemoryHistory<I> {
46 segments: BTreeMap<i64, MemorySegment<I>>,
47}
48
49impl<I> Default for RangeMemoryHistory<I> {
50 fn default() -> Self {
51 Self {
52 segments: BTreeMap::new(),
53 }
54 }
55}
56
57impl<I: Copy + Ord> RangeMemoryHistory<I> {
58 fn split_at(&mut self, point: i64) {
59 let Some((&start, segment)) = self.segments.range(..=point).next_back() else {
60 return;
61 };
62 if start == point || point >= segment.end {
63 return;
64 }
65 let tail = segment.clone();
66 self.segments
67 .get_mut(&start)
68 .expect("the selected memory segment exists")
69 .end = point;
70 self.segments.insert(point, tail);
71 }
72
73 fn read(&mut self, offset: i64, end: i64, instruction: I, dependencies: &mut BTreeSet<I>) {
74 self.split_at(offset);
75 self.split_at(end);
76
77 let existing = self
78 .segments
79 .range(offset..end)
80 .map(|(&start, segment)| (start, segment.end))
81 .collect::<Vec<_>>();
82 let mut cursor = offset;
83 for &(start, segment_end) in &existing {
84 if cursor < start {
85 self.segments.insert(
86 cursor,
87 MemorySegment {
88 end: start,
89 history: AccessHistory::default(),
90 },
91 );
92 }
93 cursor = cursor.max(segment_end);
94 }
95 if cursor < end {
96 self.segments.insert(
97 cursor,
98 MemorySegment {
99 end,
100 history: AccessHistory::default(),
101 },
102 );
103 }
104
105 let starts = self
106 .segments
107 .range(offset..end)
108 .map(|(&start, _)| start)
109 .collect::<Vec<_>>();
110 for start in starts {
111 let history = &mut self
112 .segments
113 .get_mut(&start)
114 .expect("the covered memory segment exists")
115 .history;
116 dependencies.extend(history.last_writer);
117 history.record_reader(instruction);
118 }
119 }
120
121 fn write(&mut self, offset: i64, end: i64, instruction: I, dependencies: &mut BTreeSet<I>) {
122 self.split_at(offset);
123 self.split_at(end);
124 let starts = self
125 .segments
126 .range(offset..end)
127 .map(|(&start, _)| start)
128 .collect::<Vec<_>>();
129 for start in &starts {
130 let history = &self
131 .segments
132 .get(start)
133 .expect("the overlapping memory segment exists")
134 .history;
135 dependencies.extend(history.last_writer);
136 dependencies.extend(history.readers_since_write.iter().copied());
137 }
138 for start in starts {
139 self.segments.remove(&start);
140 }
141 self.segments.insert(
142 offset,
143 MemorySegment {
144 end,
145 history: AccessHistory {
146 last_writer: Some(instruction),
147 readers_since_write: Vec::new(),
148 },
149 },
150 );
151 self.coalesce_around(offset);
152 }
153
154 fn collect_writers(&self, dependencies: &mut BTreeSet<I>) {
155 dependencies.extend(
156 self.segments
157 .values()
158 .filter_map(|segment| segment.history.last_writer),
159 );
160 }
161
162 fn collect_reads_and_writes(&self, dependencies: &mut BTreeSet<I>) {
163 for segment in self.segments.values() {
164 dependencies.extend(segment.history.last_writer);
165 dependencies.extend(segment.history.readers_since_write.iter().copied());
166 }
167 }
168
169 fn coalesce_around(&mut self, mut start: i64) {
170 let Some(mut current) = self.segments.get(&start).cloned() else {
171 return;
172 };
173 let predecessor = self
174 .segments
175 .range(..start)
176 .next_back()
177 .map(|(&other_start, other)| (other_start, other.clone()));
178 if let Some((other_start, other)) = predecessor
179 && other.end == start
180 && other.history == current.history
181 {
182 self.segments.remove(&start);
183 self.segments
184 .get_mut(&other_start)
185 .expect("the predecessor memory segment exists")
186 .end = current.end;
187 start = other_start;
188 current.end = self.segments[&start].end;
189 }
190 if let Some(successor) = self.segments.get(¤t.end).cloned()
191 && successor.history == current.history
192 {
193 self.segments.remove(¤t.end);
194 self.segments
195 .get_mut(&start)
196 .expect("the current memory segment exists")
197 .end = successor.end;
198 }
199 }
200}
201
202#[derive(Debug)]
203struct MemoryObjectHistory<I> {
204 exact: RangeMemoryHistory<I>,
205 last_unknown_writer: Option<I>,
206 unknown_readers_since_write: Vec<I>,
207}
208
209impl<I> Default for MemoryObjectHistory<I> {
210 fn default() -> Self {
211 Self {
212 exact: RangeMemoryHistory::default(),
213 last_unknown_writer: None,
214 unknown_readers_since_write: Vec::new(),
215 }
216 }
217}
218
219impl<I: Copy + Ord> MemoryObjectHistory<I> {
220 fn read_exact(
221 &mut self,
222 offset: i64,
223 end: i64,
224 instruction: I,
225 dependencies: &mut BTreeSet<I>,
226 ) {
227 dependencies.extend(self.last_unknown_writer);
228 self.exact.read(offset, end, instruction, dependencies);
229 }
230
231 fn write_exact(
232 &mut self,
233 offset: i64,
234 end: i64,
235 instruction: I,
236 dependencies: &mut BTreeSet<I>,
237 ) {
238 dependencies.extend(self.last_unknown_writer);
239 dependencies.extend(self.unknown_readers_since_write.iter().copied());
240 self.exact.write(offset, end, instruction, dependencies);
241 }
242
243 fn read_unknown(&mut self, instruction: I, dependencies: &mut BTreeSet<I>) {
244 dependencies.extend(self.last_unknown_writer);
245 self.exact.collect_writers(dependencies);
246 if self.unknown_readers_since_write.last() != Some(&instruction) {
247 self.unknown_readers_since_write.push(instruction);
248 }
249 }
250
251 fn write_unknown(&mut self, instruction: I, dependencies: &mut BTreeSet<I>) {
252 self.collect_reads_and_writes(dependencies);
253 self.exact.segments.clear();
254 self.last_unknown_writer = Some(instruction);
255 self.unknown_readers_since_write.clear();
256 }
257
258 fn collect_writers(&self, dependencies: &mut BTreeSet<I>) {
259 dependencies.extend(self.last_unknown_writer);
260 self.exact.collect_writers(dependencies);
261 }
262
263 fn collect_reads_and_writes(&self, dependencies: &mut BTreeSet<I>) {
264 dependencies.extend(self.last_unknown_writer);
265 dependencies.extend(self.unknown_readers_since_write.iter().copied());
266 self.exact.collect_reads_and_writes(dependencies);
267 }
268}
269
270#[derive(Debug)]
274pub struct MemoryDependencyTracker<O, I> {
275 objects: BTreeMap<O, MemoryObjectHistory<I>>,
276 last_global_writer: Option<I>,
277 global_readers_since_write: Vec<I>,
278}
279
280impl<O, I> Default for MemoryDependencyTracker<O, I> {
281 fn default() -> Self {
282 Self {
283 objects: BTreeMap::new(),
284 last_global_writer: None,
285 global_readers_since_write: Vec::new(),
286 }
287 }
288}
289
290impl<O: Copy + Ord, I: Copy + Ord> MemoryDependencyTracker<O, I> {
291 pub fn add_event<R, W>(
295 &mut self,
296 instruction: I,
297 reads: R,
298 writes: W,
299 dependencies: &mut BTreeSet<I>,
300 ) where
301 R: IntoIterator<Item = MemoryEffect<O>>,
302 W: IntoIterator<Item = MemoryEffect<O>>,
303 {
304 for effect in reads {
305 self.read(effect, instruction, dependencies);
306 }
307 for effect in writes {
308 self.write(effect, instruction, dependencies);
309 }
310 dependencies.remove(&instruction);
311 }
312
313 fn read(&mut self, effect: MemoryEffect<O>, instruction: I, dependencies: &mut BTreeSet<I>) {
314 match effect {
315 MemoryEffect::Exact(location) => {
316 if location.byte_len == 0 {
317 return;
318 }
319 dependencies.extend(self.last_global_writer);
320 match location.end() {
321 Some(end) => self.objects.entry(location.object).or_default().read_exact(
322 location.offset,
323 end,
324 instruction,
325 dependencies,
326 ),
327 None => self
328 .objects
329 .entry(location.object)
330 .or_default()
331 .read_unknown(instruction, dependencies),
332 }
333 }
334 MemoryEffect::UnknownObject(object) => {
335 dependencies.extend(self.last_global_writer);
336 self.objects
337 .entry(object)
338 .or_default()
339 .read_unknown(instruction, dependencies);
340 }
341 MemoryEffect::UnknownAll => {
342 dependencies.extend(self.last_global_writer);
343 for history in self.objects.values() {
344 history.collect_writers(dependencies);
345 }
346 if self.global_readers_since_write.last() != Some(&instruction) {
347 self.global_readers_since_write.push(instruction);
348 }
349 }
350 }
351 }
352
353 fn write(&mut self, effect: MemoryEffect<O>, instruction: I, dependencies: &mut BTreeSet<I>) {
354 match effect {
355 MemoryEffect::Exact(location) => {
356 if location.byte_len == 0 {
357 return;
358 }
359 self.collect_global_history(dependencies);
360 match location.end() {
361 Some(end) => self
362 .objects
363 .entry(location.object)
364 .or_default()
365 .write_exact(location.offset, end, instruction, dependencies),
366 None => self
367 .objects
368 .entry(location.object)
369 .or_default()
370 .write_unknown(instruction, dependencies),
371 }
372 }
373 MemoryEffect::UnknownObject(object) => {
374 self.collect_global_history(dependencies);
375 self.objects
376 .entry(object)
377 .or_default()
378 .write_unknown(instruction, dependencies);
379 }
380 MemoryEffect::UnknownAll => {
381 self.collect_global_history(dependencies);
382 for history in self.objects.values() {
383 history.collect_reads_and_writes(dependencies);
384 }
385 self.objects.clear();
386 self.last_global_writer = Some(instruction);
387 self.global_readers_since_write.clear();
388 }
389 }
390 }
391
392 fn collect_global_history(&self, dependencies: &mut BTreeSet<I>) {
393 dependencies.extend(self.last_global_writer);
394 dependencies.extend(self.global_readers_since_write.iter().copied());
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401 use crate::memory::MemoryLocation;
402
403 fn exact(object: u8, offset: i64, byte_len: usize) -> MemoryEffect<u8> {
404 MemoryEffect::Exact(MemoryLocation {
405 object,
406 offset,
407 byte_len,
408 })
409 }
410
411 fn add(
412 tracker: &mut MemoryDependencyTracker<u8, usize>,
413 instruction: usize,
414 reads: Vec<MemoryEffect<u8>>,
415 writes: Vec<MemoryEffect<u8>>,
416 ) -> BTreeSet<usize> {
417 let mut dependencies = BTreeSet::new();
418 tracker.add_event(instruction, reads, writes, &mut dependencies);
419 dependencies
420 }
421
422 #[test]
423 fn emits_raw_war_and_waw_but_no_read_after_read_edge() {
424 let mut tracker = MemoryDependencyTracker::default();
425
426 assert!(add(&mut tracker, 0, vec![exact(0, 0, 8)], vec![]).is_empty());
427 assert!(add(&mut tracker, 1, vec![exact(0, 4, 4)], vec![]).is_empty());
428 assert_eq!(
429 add(&mut tracker, 2, vec![], vec![exact(0, 0, 8)]),
430 BTreeSet::from([0, 1])
431 );
432 assert_eq!(
433 add(&mut tracker, 3, vec![exact(0, 7, 1)], vec![]),
434 BTreeSet::from([2])
435 );
436 assert_eq!(
437 add(&mut tracker, 4, vec![], vec![exact(0, 0, 8)]),
438 BTreeSet::from([2, 3])
439 );
440 }
441
442 #[test]
443 fn disjoint_ranges_and_objects_have_independent_histories() {
444 let mut tracker = MemoryDependencyTracker::default();
445 assert!(add(&mut tracker, 0, vec![], vec![exact(0, 0, 8)]).is_empty());
446 assert!(add(&mut tracker, 1, vec![exact(0, 8, 8)], vec![]).is_empty());
447 assert!(add(&mut tracker, 2, vec![exact(1, 0, 8)], vec![]).is_empty());
448 assert_eq!(
449 add(&mut tracker, 3, vec![exact(0, 7, 2)], vec![]),
450 BTreeSet::from([0])
451 );
452 }
453
454 #[test]
455 fn unknown_object_aliases_only_its_object() {
456 let mut tracker = MemoryDependencyTracker::default();
457 assert!(add(&mut tracker, 0, vec![], vec![exact(0, 0, 8)]).is_empty());
458 assert!(
459 add(
460 &mut tracker,
461 1,
462 vec![MemoryEffect::UnknownObject(1)],
463 vec![]
464 )
465 .is_empty()
466 );
467 assert_eq!(
468 add(
469 &mut tracker,
470 2,
471 vec![MemoryEffect::UnknownObject(0)],
472 vec![]
473 ),
474 BTreeSet::from([0])
475 );
476 assert_eq!(
477 add(&mut tracker, 3, vec![], vec![exact(0, 32, 8)]),
478 BTreeSet::from([2])
479 );
480 }
481
482 #[test]
483 fn unknown_all_orders_every_object_and_future_access() {
484 let mut tracker = MemoryDependencyTracker::default();
485 assert!(add(&mut tracker, 0, vec![], vec![exact(0, 0, 8)]).is_empty());
486 assert!(add(&mut tracker, 1, vec![exact(1, 0, 8)], vec![]).is_empty());
487 assert_eq!(
488 add(&mut tracker, 2, vec![MemoryEffect::UnknownAll], vec![]),
489 BTreeSet::from([0])
490 );
491 assert_eq!(
492 add(&mut tracker, 3, vec![], vec![MemoryEffect::UnknownAll]),
493 BTreeSet::from([0, 1, 2])
494 );
495 assert_eq!(
496 add(&mut tracker, 4, vec![exact(9, 0, 1)], vec![]),
497 BTreeSet::from([3])
498 );
499 }
500
501 #[test]
502 fn read_modify_write_does_not_emit_a_self_edge() {
503 let mut tracker = MemoryDependencyTracker::default();
504 assert!(add(&mut tracker, 0, vec![exact(0, 0, 8)], vec![exact(0, 0, 8)]).is_empty());
505 assert_eq!(
506 add(&mut tracker, 1, vec![exact(0, 0, 8)], vec![]),
507 BTreeSet::from([0])
508 );
509 }
510
511 #[test]
512 fn storage_scales_with_endpoints_instead_of_range_width() {
513 const WRITES: usize = 256;
514 const HUGE_RANGE: usize = 4_000_000;
515 let mut tracker = MemoryDependencyTracker::default();
516 for instruction in 0..WRITES {
517 let mut dependencies = BTreeSet::new();
518 tracker.add_event(
519 instruction,
520 [],
521 [
522 exact(0, 0, 8),
523 exact(0, 1024 + instruction as i64, 1),
524 exact(0, 1_000_000, HUGE_RANGE),
525 ],
526 &mut dependencies,
527 );
528 if instruction != 0 {
529 assert!(dependencies.contains(&(instruction - 1)));
530 }
531 }
532
533 let segments = tracker.objects[&0].exact.segments.len();
534 assert_eq!(segments, WRITES + 2);
535 assert!(segments < HUGE_RANGE / 1000);
536 }
537
538 #[test]
539 fn overflowing_exact_range_falls_back_to_unknown_object() {
540 let mut tracker = MemoryDependencyTracker::default();
541 assert!(add(&mut tracker, 0, vec![], vec![exact(0, 0, 8)]).is_empty());
542 assert_eq!(
543 add(&mut tracker, 1, vec![exact(0, i64::MAX, 2)], vec![]),
544 BTreeSet::from([0])
545 );
546 }
547}