logicaffeine_compile/codegen_cuda/lane/
branches.rs1use super::arena::{self, Arena};
9use super::plan::{Schedule, Touch};
10
11pub const STREAMS: usize = 4;
13
14#[derive(Clone, Debug, Default, PartialEq, Eq)]
16pub struct Branched {
17 pub stream: Vec<usize>,
19 pub waits: Vec<Vec<usize>>,
21 pub records: Vec<Option<usize>>,
23 pub sides: Vec<usize>,
25 pub joins: Vec<usize>,
27 pub events: usize,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
33pub enum Region {
34 Arena(usize, usize),
35 Named(String),
36}
37
38impl Region {
39 pub fn overlaps(&self, other: &Region) -> bool {
40 match (self, other) {
41 (Region::Arena(a0, a1), Region::Arena(b0, b1)) => a0 < b1 && b0 < a1,
42 (Region::Named(a), Region::Named(b)) => a == b,
43 _ => false,
44 }
45 }
46}
47
48fn region(s: &Schedule, arena: &Arena, t: &str, touch: Touch) -> Region {
50 let name = arena::home(s, t, touch);
51 match arena.at(name, touch) {
52 Some(p) => Region::Arena(p.offset, p.offset + p.bytes),
53 None => Region::Named(format!("{name}/{touch:?}")),
54 }
55}
56
57pub fn regions(s: &Schedule, arena: &Arena, i: usize) -> (Vec<Region>, Vec<Region>) {
59 let l = &s.launches[i];
60 let (r, w) = l.touches(s.numerics);
61 let reads: Vec<Region> = r.into_iter().map(|(t, touch)| region(s, arena, t, touch)).collect();
62 let mut writes: Vec<Region> = w.into_iter().map(|(t, touch)| region(s, arena, t, touch)).collect();
63 let owner = l.writes().first().map_or(String::new(), |t| t.to_string());
64 for sc in arena::scratch(l, s.numerics) {
65 writes.push(match arena.scratch_at(i, sc.tag) {
66 Some(p) if !sc.counter => Region::Arena(p.offset, p.offset + p.bytes),
67 _ => Region::Named(format!("{}#{owner}", sc.tag)),
68 });
69 }
70 (reads, writes)
71}
72
73fn conflict(a: &(Vec<Region>, Vec<Region>), b: &(Vec<Region>, Vec<Region>)) -> bool {
75 let hits = |x: &[Region], y: &[Region]| x.iter().any(|p| y.iter().any(|q| p.overlaps(q)));
76 hits(&a.1, &b.0) || hits(&a.1, &b.1) || hits(&a.0, &b.1)
77}
78
79pub fn branch(s: &Schedule, arena: &Arena) -> Branched {
81 let n = s.launches.len();
82 let mem: Vec<(Vec<Region>, Vec<Region>)> = (0..n).map(|i| regions(s, arena, i)).collect();
83 let mut clock: Vec<[usize; STREAMS]> = Vec::with_capacity(n);
85 let mut last: [Option<usize>; STREAMS] = [None; STREAMS];
86 let mut stream_clock: [[usize; STREAMS]; STREAMS] = [[0; STREAMS]; STREAMS];
87 let mut out = Branched { stream: vec![0; n], waits: vec![Vec::new(); n], records: vec![None; n], ..Default::default() };
88 for j in 0..n {
89 let preds: Vec<usize> = (0..j).filter(|&i| conflict(&mem[i], &mem[j])).collect();
90 let follows = |t: usize| match last[t] {
93 None => true,
94 Some(l) => preds.iter().any(|&p| p == l || clock[p][t] > l),
95 };
96 let chained = preds.iter().copied().max().map(|p| out.stream[p]).filter(|&t| last[t] == preds.iter().copied().max());
97 let t = chained
98 .or_else(|| (0..STREAMS).filter(|&t| follows(t)).max_by_key(|&t| last[t].map_or(0, |l| l + 1)))
99 .unwrap_or_else(|| (0..STREAMS).min_by_key(|&t| last[t].map_or(0, |l| l + 1)).expect("a stream"));
100 let mut c = stream_clock[t];
101 for &p in preds.iter().rev() {
103 let ps = out.stream[p];
104 if ps != t && c[ps] <= p {
105 let e = *out.records[p].get_or_insert_with(|| {
106 out.events += 1;
107 out.events - 1
108 });
109 if !out.waits[j].contains(&e) {
110 out.waits[j].push(e);
111 }
112 for (u, v) in c.iter_mut().zip(clock[p]) {
113 *u = (*u).max(v);
114 }
115 c[ps] = c[ps].max(p + 1);
116 }
117 }
118 c[t] = j + 1;
119 out.stream[j] = t;
120 stream_clock[t] = c;
121 last[t] = Some(j);
122 clock.push(c);
123 if t != 0 && !out.sides.contains(&t) {
124 out.sides.push(t);
125 }
126 }
127 out.sides.sort_unstable();
128 for &t in &out.sides {
129 let l = last[t].expect("a side stream's launch");
130 let e = *out.records[l].get_or_insert_with(|| {
131 out.events += 1;
132 out.events - 1
133 });
134 out.joins.push(e);
135 }
136 out
137}