1use crate::layout::ElemAlignment;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum VectorRequirement<O> {
19 Access { operand: O, elements: usize },
22 Elementwise { operand: O },
25 Masked { operand: O },
29 Together { operand: O, with: O },
33 Aligned {
36 operand: O,
37 alignment: ElemAlignment,
38 },
39}
40
41impl<O: Copy + Eq> VectorRequirement<O> {
42 pub fn operand(&self) -> O {
44 match self {
45 VectorRequirement::Access { operand, .. }
46 | VectorRequirement::Elementwise { operand }
47 | VectorRequirement::Masked { operand }
48 | VectorRequirement::Together { operand, .. }
49 | VectorRequirement::Aligned { operand, .. } => *operand,
50 }
51 }
52
53 pub fn restated<P>(self, operand: impl Fn(O) -> Option<P>) -> Option<VectorRequirement<P>> {
57 Some(match self {
58 VectorRequirement::Access {
59 operand: at,
60 elements,
61 } => VectorRequirement::Access {
62 operand: operand(at)?,
63 elements,
64 },
65 VectorRequirement::Elementwise { operand: at } => VectorRequirement::Elementwise {
66 operand: operand(at)?,
67 },
68 VectorRequirement::Masked { operand: at } => VectorRequirement::Masked {
69 operand: operand(at)?,
70 },
71 VectorRequirement::Together { operand: at, with } => VectorRequirement::Together {
72 operand: operand(at)?,
73 with: operand(with)?,
74 },
75 VectorRequirement::Aligned {
76 operand: at,
77 alignment,
78 } => VectorRequirement::Aligned {
79 operand: operand(at)?,
80 alignment,
81 },
82 })
83 }
84
85 pub fn admits(&self, width: usize) -> bool {
87 match self {
88 VectorRequirement::Access { elements, .. } => elements.is_multiple_of(width),
89 VectorRequirement::Elementwise { .. } | VectorRequirement::Masked { .. } => width == 1,
90 VectorRequirement::Aligned { alignment, .. } => alignment.admits(width),
91 VectorRequirement::Together { .. } => true,
93 }
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
102pub struct Vectorization<O> {
103 requirements: Vec<VectorRequirement<O>>,
104}
105
106impl<O: Copy + Eq> Vectorization<O> {
107 pub fn new() -> Self {
108 Self {
109 requirements: Vec::new(),
110 }
111 }
112
113 pub fn require(&mut self, requirement: VectorRequirement<O>) {
114 self.requirements.push(requirement);
115 }
116
117 pub fn restate<S: Copy + Eq>(
121 &mut self,
122 shared: Vectorization<S>,
123 operand: impl Fn(S) -> Option<O>,
124 ) {
125 let restated = shared
126 .requirements
127 .into_iter()
128 .filter_map(|requirement| requirement.restated(&operand));
129 self.requirements.extend(restated);
130 }
131
132 pub fn width(&self, operand: O, widest: impl Fn(O) -> usize) -> usize {
136 let mut lined = vec![operand];
137 let mut next = 0;
138 while let Some(&at) = lined.get(next) {
139 for requirement in &self.requirements {
140 let VectorRequirement::Together { operand, with } = *requirement else {
141 continue;
142 };
143 let other = match (operand == at, with == at) {
144 (true, _) => with,
145 (_, true) => operand,
146 (false, false) => continue,
147 };
148 if !lined.contains(&other) {
149 lined.push(other);
150 }
151 }
152 next += 1;
153 }
154 lined
155 .into_iter()
156 .map(|member| self.alone(member, widest(member)))
157 .min()
158 .unwrap_or(1)
159 }
160
161 fn alone(&self, operand: O, widest: usize) -> usize {
163 let admitted = |width: usize| {
164 self.requirements
165 .iter()
166 .filter(|requirement| requirement.operand() == operand)
167 .all(|requirement| requirement.admits(width))
168 };
169 let mut width = widest.max(1).next_power_of_two();
170 while width > 1 && (width > widest || !admitted(width)) {
171 width /= 2;
172 }
173 width
174 }
175}
176
177impl<O: Copy + Eq> Default for Vectorization<O> {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
190 fn operands_lined_together_share_the_narrowest_width() {
191 let mut vectorization = Vectorization::new();
192 vectorization.require(VectorRequirement::Access {
193 operand: 'c',
194 elements: 2,
195 });
196 vectorization.require(VectorRequirement::Together {
197 operand: 'a',
198 with: 'b',
199 });
200 vectorization.require(VectorRequirement::Together {
201 operand: 'b',
202 with: 'c',
203 });
204 for operand in ['a', 'b', 'c'] {
205 assert_eq!(vectorization.width(operand, |_| 8), 2, "{operand}");
206 }
207 assert_eq!(vectorization.width('d', |_| 8), 8);
208 }
209
210 #[test]
213 fn the_width_reads_the_buffer_whole_lines() {
214 let mut vectorization = Vectorization::new();
215 vectorization.require(VectorRequirement::Aligned {
216 operand: 'a',
217 alignment: ElemAlignment::new(&[4, 12], &[12, 1]),
218 });
219 assert_eq!(vectorization.width('a', |_| 8), 4);
220 vectorization.require(VectorRequirement::Aligned {
221 operand: 'b',
222 alignment: ElemAlignment::ONE,
223 });
224 assert_eq!(vectorization.width('b', |_| 8), 1);
225 }
226
227 #[test]
230 fn a_restated_requirement_lands_on_the_bound_operand() {
231 let mut shared = Vectorization::new();
232 shared.require(VectorRequirement::Access {
233 operand: 'l',
234 elements: 4,
235 });
236 shared.require(VectorRequirement::Access {
237 operand: 'o',
238 elements: 1,
239 });
240 shared.require(VectorRequirement::Together {
241 operand: 'r',
242 with: 'o',
243 });
244 let mut family = Vectorization::new();
245 family.restate(shared, |operand| match operand {
246 'l' => Some('a'),
247 'r' => Some('b'),
248 _ => None,
249 });
250 assert_eq!(family.width('a', |_| 8), 4);
251 assert_eq!(family.width('b', |_| 8), 8);
252 }
253
254 #[test]
255 fn the_width_divides_every_access() {
256 let mut vectorization = Vectorization::new();
257 vectorization.require(VectorRequirement::Access {
258 operand: 'a',
259 elements: 24,
260 });
261 assert_eq!(vectorization.width('a', |_| 16), 8);
262 vectorization.require(VectorRequirement::Access {
263 operand: 'a',
264 elements: 12,
265 });
266 assert_eq!(vectorization.width('a', |_| 16), 4);
267 assert_eq!(vectorization.width('b', |_| 16), 16);
268 }
269
270 #[test]
271 fn an_elementwise_access_is_scalar() {
272 let mut vectorization = Vectorization::new();
273 vectorization.require(VectorRequirement::Access {
274 operand: 'a',
275 elements: 8,
276 });
277 vectorization.require(VectorRequirement::Elementwise { operand: 'a' });
278 assert_eq!(vectorization.width('a', |_| 8), 1);
279 }
280
281 #[test]
282 fn the_width_never_exceeds_the_widest_it_is_given() {
283 let vectorization = Vectorization::<char>::new();
284 assert_eq!(vectorization.width('a', |_| 2), 2);
285 assert_eq!(vectorization.width('a', |_| 3), 2);
286 assert_eq!(vectorization.width('a', |_| 1), 1);
287 }
288}