logicaffeine_compile/spatial/passes/
pack.rs1use std::collections::BTreeSet;
16
17use crate::spatial::ir::*;
18use crate::spatial::target::{Interconnect, SpatialTarget};
19use crate::spatial::tensor_parallel::SectionTouch;
20
21pub fn for_each_site_mut(p: &mut SpatialProgram, f: &mut dyn FnMut(&mut Site)) {
25 fn posts(ops: &mut [TaggedOp], f: &mut dyn FnMut(&mut Site)) {
26 for t in ops {
27 match &mut t.op {
28 ChoreoOp::SemInc { at, .. } => f(at),
29 ChoreoOp::Loop { body, .. } => posts(body, f),
30 _ => {}
31 }
32 }
33 }
34 for s in &mut p.stages {
35 if let Some(x) = &mut s.site {
36 f(x);
37 }
38 }
39 for b in &mut p.buffers {
40 if let Some(x) = &mut b.site {
41 f(x);
42 }
43 }
44 if let Some(pl) = &mut p.placement {
45 pl.site_of.iter_mut().for_each(|x| f(x));
46 pl.engine_of.iter_mut().for_each(|e| f(&mut e.site));
47 pl.items_of_site.iter_mut().for_each(|(x, _)| f(x));
48 }
49 if let Some(mm) = &mut p.memory {
50 mm.buffer_at.iter_mut().for_each(|a| f(&mut a.site));
51 }
52 for sem in &mut p.semaphores {
53 sem.sites.iter_mut().for_each(|x| f(x));
54 }
55 if let Some(sch) = &mut p.schedule {
56 for prog in &mut sch.programs {
57 f(&mut prog.site);
58 posts(&mut prog.ops, f);
59 }
60 }
61}
62
63pub fn sites_used(p: &mut SpatialProgram) -> BTreeSet<Site> {
65 let mut seen = BTreeSet::new();
66 for_each_site_mut(p, &mut |s| {
67 seen.insert(*s);
68 });
69 seen
70}
71
72fn packable(sections: &mut [SpatialProgram], target: &SpatialTarget) -> bool {
75 !target.provides_interconnect(Interconnect::MulticastRect)
76 && sections.iter_mut().all(|p| {
77 p.placement.as_ref().is_some_and(|pl| pl.groups.is_empty())
78 && sites_used(p).iter().all(|s| s.chip == 0)
79 })
80}
81
82pub fn pack_phases(
89 mut sections: Vec<SpatialProgram>,
90 target: &SpatialTarget,
91) -> Result<(Vec<SpatialProgram>, Vec<bool>), Vec<SpatialProgram>> {
92 if sections.len() < 2 || !packable(&mut sections, target) {
93 return Err(sections);
94 }
95 let mesh: Vec<Site> = {
96 let mut all: Vec<Site> = (0..target.sites.x)
97 .flat_map(|x| (0..target.sites.y).map(move |y| Site::new(x, y)))
98 .collect();
99 all.sort();
100 all
101 };
102 let footprints: Vec<Vec<Site>> = sections
103 .iter_mut()
104 .map(|p| sites_used(p).into_iter().collect())
105 .collect();
106 if footprints.iter().any(|used| used.len() > mesh.len()) {
107 return Err(sections);
108 }
109 let (mut written_in, mut read_in) = (
110 std::collections::BTreeMap::<String, usize>::new(),
111 std::collections::BTreeMap::<String, usize>::new(),
112 );
113 let mut occupied: Vec<usize> = Vec::new();
114 let mut phase_of = Vec::with_capacity(sections.len());
115 for (p, used) in sections.iter_mut().zip(&footprints) {
116 let touch = SectionTouch::of(p);
117 let raw = touch.reads.iter().filter_map(|n| written_in.get(n));
118 let war_waw = touch
119 .writes
120 .iter()
121 .flat_map(|n| [written_in.get(n), read_in.get(n)])
122 .flatten();
123 let after = raw.chain(war_waw).map(|ph| ph + 1).max().unwrap_or(0);
124 let ph = (after..)
126 .find(|ph| occupied.get(*ph).copied().unwrap_or(0) + used.len() <= mesh.len())
127 .expect("an empty phase holds any placed section");
128 if occupied.len() <= ph {
129 occupied.resize(ph + 1, 0);
130 }
131 let target_sites = &mesh[occupied[ph]..occupied[ph] + used.len()];
134 let rename: std::collections::BTreeMap<Site, Site> = used
135 .iter()
136 .copied()
137 .zip(target_sites.iter().copied())
138 .collect();
139 for_each_site_mut(p, &mut |s| *s = rename[s]);
140 occupied[ph] += used.len();
141 phase_of.push(ph);
142 for n in touch.reads {
143 let r = read_in.entry(n).or_insert(ph);
144 *r = (*r).max(ph);
145 }
146 for n in touch.writes {
147 let w = written_in.entry(n).or_insert(ph);
148 *w = (*w).max(ph);
149 }
150 }
151 let mut order: Vec<usize> = (0..sections.len()).collect();
152 order.sort_by_key(|&k| (phase_of[k], k));
153 let barrier_after = order
154 .iter()
155 .enumerate()
156 .map(|(pos, &k)| {
157 order
158 .get(pos + 1)
159 .is_some_and(|&next| phase_of[next] != phase_of[k])
160 })
161 .collect();
162 let mut slots: Vec<Option<SpatialProgram>> = sections.into_iter().map(Some).collect();
163 Ok((
164 order
165 .iter()
166 .map(|&k| slots[k].take().expect("each section once"))
167 .collect(),
168 barrier_after,
169 ))
170}