1use smallvec::SmallVec;
17
18#[derive(Debug, Clone, PartialEq, Eq, Default)]
24pub struct BindingSlotSet {
25 slots: SmallVec<[u32; 8]>,
26}
27
28impl BindingSlotSet {
29 pub fn insert(&mut self, slot: u32) -> bool {
31 match self.slots.binary_search(&slot) {
32 Ok(_) => false,
33 Err(pos) => {
34 self.slots.insert(pos, slot);
35 true
36 }
37 }
38 }
39
40 #[must_use]
42 pub fn contains(&self, slot: &u32) -> bool {
43 self.slots.binary_search(slot).is_ok()
44 }
45
46 #[must_use]
48 pub fn intersects(&self, other: &Self) -> bool {
49 let (small, large) = if self.slots.len() <= other.slots.len() {
50 (self, other)
51 } else {
52 (other, self)
53 };
54 small.slots.iter().any(|slot| large.contains(slot))
55 }
56}
57
58impl FromIterator<u32> for BindingSlotSet {
59 fn from_iter<T: IntoIterator<Item = u32>>(iter: T) -> Self {
60 let mut set = Self::default();
61 for slot in iter {
62 set.insert(slot);
63 }
64 set
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct ArmBindingSummary {
73 pub reads: BindingSlotSet,
76 pub writes: BindingSlotSet,
79}
80
81impl ArmBindingSummary {
82 #[must_use]
85 pub fn new() -> Self {
86 Self::default()
87 }
88}
89
90impl Default for ArmBindingSummary {
91 fn default() -> Self {
92 Self {
93 reads: BindingSlotSet::default(),
94 writes: BindingSlotSet::default(),
95 }
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub enum ArmIndependenceVerdict {
102 Independent,
105 SerializeRequired {
109 reason: ArmConflict,
113 },
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub enum ArmConflict {
119 WriteWriteConflict,
122 ReadAfterWrite,
126 WriteAfterRead,
129}
130
131#[must_use]
139pub fn can_dispatch_concurrently(
140 a: &ArmBindingSummary,
141 b: &ArmBindingSummary,
142) -> ArmIndependenceVerdict {
143 if a.writes.intersects(&b.writes) {
144 return ArmIndependenceVerdict::SerializeRequired {
145 reason: ArmConflict::WriteWriteConflict,
146 };
147 }
148 if a.writes.intersects(&b.reads) {
149 return ArmIndependenceVerdict::SerializeRequired {
150 reason: ArmConflict::ReadAfterWrite,
151 };
152 }
153 if a.reads.intersects(&b.writes) {
154 return ArmIndependenceVerdict::SerializeRequired {
155 reason: ArmConflict::WriteAfterRead,
156 };
157 }
158 ArmIndependenceVerdict::Independent
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 fn summary(reads: &[u32], writes: &[u32]) -> ArmBindingSummary {
166 ArmBindingSummary {
167 reads: reads.iter().copied().collect(),
168 writes: writes.iter().copied().collect(),
169 }
170 }
171
172 #[test]
173 fn fully_disjoint_arms_are_independent() {
174 let a = summary(&[0, 1], &[2]);
175 let b = summary(&[3, 4], &[5]);
176 assert_eq!(
177 can_dispatch_concurrently(&a, &b),
178 ArmIndependenceVerdict::Independent
179 );
180 }
181
182 #[test]
183 fn empty_arms_are_independent() {
184 let a = summary(&[], &[]);
185 let b = summary(&[], &[]);
186 assert_eq!(
187 can_dispatch_concurrently(&a, &b),
188 ArmIndependenceVerdict::Independent
189 );
190 }
191
192 #[test]
193 fn shared_read_only_slot_is_independent() {
194 let a = summary(&[7], &[1]);
195 let b = summary(&[7], &[2]);
196 assert_eq!(
198 can_dispatch_concurrently(&a, &b),
199 ArmIndependenceVerdict::Independent
200 );
201 }
202
203 #[test]
204 fn write_write_conflict_serialises() {
205 let a = summary(&[], &[3]);
206 let b = summary(&[], &[3]);
207 assert_eq!(
208 can_dispatch_concurrently(&a, &b),
209 ArmIndependenceVerdict::SerializeRequired {
210 reason: ArmConflict::WriteWriteConflict,
211 }
212 );
213 }
214
215 #[test]
216 fn read_after_write_serialises() {
217 let a = summary(&[0], &[5]);
218 let b = summary(&[5], &[1]);
219 assert_eq!(
220 can_dispatch_concurrently(&a, &b),
221 ArmIndependenceVerdict::SerializeRequired {
222 reason: ArmConflict::ReadAfterWrite,
223 }
224 );
225 }
226
227 #[test]
228 fn write_after_read_serialises() {
229 let a = summary(&[5], &[1]);
230 let b = summary(&[0], &[5]);
231 assert_eq!(
232 can_dispatch_concurrently(&a, &b),
233 ArmIndependenceVerdict::SerializeRequired {
234 reason: ArmConflict::WriteAfterRead,
235 }
236 );
237 }
238
239 #[test]
240 fn write_write_takes_precedence_over_other_conflicts() {
241 let a = summary(&[], &[1, 3]);
244 let b = summary(&[1], &[3]);
245 assert_eq!(
246 can_dispatch_concurrently(&a, &b),
247 ArmIndependenceVerdict::SerializeRequired {
248 reason: ArmConflict::WriteWriteConflict,
249 }
250 );
251 }
252
253 #[test]
254 fn verdict_is_symmetric_for_writes_and_reads() {
255 let a = summary(&[], &[10]);
256 let b = summary(&[], &[10]);
257 let verdict_ab = can_dispatch_concurrently(&a, &b);
259 let verdict_ba = can_dispatch_concurrently(&b, &a);
260 assert_eq!(verdict_ab, verdict_ba);
261 }
262
263 #[test]
264 fn one_empty_arm_leaves_independent_when_other_alone() {
265 let a = summary(&[1, 2, 3], &[4]);
266 let b = ArmBindingSummary::new();
267 assert_eq!(
268 can_dispatch_concurrently(&a, &b),
269 ArmIndependenceVerdict::Independent
270 );
271 assert_eq!(
272 can_dispatch_concurrently(&b, &a),
273 ArmIndependenceVerdict::Independent
274 );
275 }
276}