1use std::fmt;
22
23use crate::Opcode;
24
25#[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
30pub struct Flags(u16);
31
32impl Flags {
33 pub const NONE: Self = Self(0);
35
36 pub const NSW: Self = Self(1 << 0);
39 pub const NUW: Self = Self(1 << 1);
42 pub const EXACT: Self = Self(1 << 2);
44
45 pub const NNAN: Self = Self(1 << 3);
47 pub const NINF: Self = Self(1 << 4);
49 pub const NSZ: Self = Self(1 << 5);
51 pub const ARCP: Self = Self(1 << 6);
53 pub const CONTRACT: Self = Self(1 << 7);
55 pub const REASSOC: Self = Self(1 << 8);
57
58 pub const VOLATILE: Self = Self(1 << 9);
60 pub const NOALIAS: Self = Self(1 << 10);
62
63 pub const FAST: Self = Self(
65 Self::NNAN.0
66 | Self::NINF.0
67 | Self::NSZ.0
68 | Self::ARCP.0
69 | Self::CONTRACT.0
70 | Self::REASSOC.0,
71 );
72
73 #[must_use]
75 pub const fn bits(self) -> u16 {
76 self.0
77 }
78
79 #[must_use]
81 pub const fn is_empty(self) -> bool {
82 self.0 == 0
83 }
84
85 #[must_use]
87 pub const fn contains(self, other: Self) -> bool {
88 self.0 & other.0 == other.0
89 }
90
91 #[must_use]
93 pub const fn union(self, other: Self) -> Self {
94 Self(self.0 | other.0)
95 }
96
97 #[must_use]
102 pub const fn intersection(self, other: Self) -> Self {
103 Self(self.0 & other.0)
104 }
105
106 #[must_use]
108 pub const fn without(self, other: Self) -> Self {
109 Self(self.0 & !other.0)
110 }
111
112 #[must_use]
118 pub const fn legal_on(opcode: Opcode) -> Self {
119 match opcode {
120 Opcode::Add | Opcode::Sub | Opcode::Mul | Opcode::Shl => Self::NSW.union(Self::NUW),
121 Opcode::SDiv | Opcode::UDiv | Opcode::LShr | Opcode::AShr => Self::EXACT,
122 Opcode::FAdd
123 | Opcode::FSub
124 | Opcode::FMul
125 | Opcode::FDiv
126 | Opcode::FRem
127 | Opcode::FNeg
128 | Opcode::Fma
129 | Opcode::FCmp => Self::FAST,
130 Opcode::Load | Opcode::Store | Opcode::Memcpy | Opcode::Memmove | Opcode::Memset => {
131 Self::VOLATILE
132 }
133 Opcode::InlineAsm => Self::VOLATILE,
134 Opcode::Alloca | Opcode::PtrAdd => Self::NOALIAS,
135 _ => Self::NONE,
136 }
137 }
138
139 pub fn iter(self) -> impl Iterator<Item = (Self, &'static str)> {
141 NAMED.iter().copied().filter(move |&(flag, _)| self.contains(flag))
142 }
143
144 #[must_use]
146 pub fn from_name(name: &str) -> Option<Self> {
147 NAMED.iter().find(|&&(_, named)| named == name).map(|&(flag, _)| flag)
148 }
149}
150
151impl std::ops::BitOr for Flags {
152 type Output = Self;
153
154 fn bitor(self, other: Self) -> Self {
155 self.union(other)
156 }
157}
158
159impl std::ops::BitOrAssign for Flags {
160 fn bitor_assign(&mut self, other: Self) {
161 *self = self.union(other);
162 }
163}
164
165impl fmt::Display for Flags {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 for (_, name) in self.iter() {
170 write!(f, ".{name}")?;
171 }
172 Ok(())
173 }
174}
175
176impl fmt::Debug for Flags {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 if self.is_empty() {
179 return f.write_str("Flags::NONE");
180 }
181 fmt::Display::fmt(self, f)
182 }
183}
184
185static NAMED: &[(Flags, &str)] = &[
187 (Flags::NSW, "nsw"),
188 (Flags::NUW, "nuw"),
189 (Flags::EXACT, "exact"),
190 (Flags::NNAN, "nnan"),
191 (Flags::NINF, "ninf"),
192 (Flags::NSZ, "nsz"),
193 (Flags::ARCP, "arcp"),
194 (Flags::CONTRACT, "contract"),
195 (Flags::REASSOC, "reassoc"),
196 (Flags::VOLATILE, "volatile"),
197 (Flags::NOALIAS, "noalias"),
198];
199
200#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
205pub enum MemOrder {
206 #[default]
208 NotAtomic,
209 Relaxed,
211 Acquire,
213 Release,
215 AcqRel,
217 SeqCst,
219}
220
221impl MemOrder {
222 #[must_use]
224 pub const fn name(self) -> &'static str {
225 match self {
226 Self::NotAtomic => "not_atomic",
227 Self::Relaxed => "relaxed",
228 Self::Acquire => "acquire",
229 Self::Release => "release",
230 Self::AcqRel => "acq_rel",
231 Self::SeqCst => "seq_cst",
232 }
233 }
234
235 #[must_use]
237 pub fn from_name(name: &str) -> Option<Self> {
238 Self::all().find(|order| order.name() == name)
239 }
240
241 pub fn all() -> impl Iterator<Item = Self> {
243 [Self::NotAtomic, Self::Relaxed, Self::Acquire, Self::Release, Self::AcqRel, Self::SeqCst]
244 .into_iter()
245 }
246
247 #[must_use]
251 pub const fn is_valid_for_load(self) -> bool {
252 matches!(self, Self::Relaxed | Self::Acquire | Self::SeqCst)
253 }
254
255 #[must_use]
259 pub const fn is_valid_for_store(self) -> bool {
260 matches!(self, Self::Relaxed | Self::Release | Self::SeqCst)
261 }
262
263 #[must_use]
265 pub const fn is_valid_for_rmw(self) -> bool {
266 !matches!(self, Self::NotAtomic)
267 }
268}
269
270impl fmt::Display for MemOrder {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 f.write_str(self.name())
273 }
274}
275
276#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
278pub enum RmwOp {
279 Xchg,
281 Add,
283 Sub,
285 And,
287 Nand,
289 Or,
291 Xor,
293 SMax,
295 SMin,
297 UMax,
299 UMin,
301 FAdd,
303 FSub,
305}
306
307impl RmwOp {
308 #[must_use]
310 pub const fn name(self) -> &'static str {
311 match self {
312 Self::Xchg => "xchg",
313 Self::Add => "add",
314 Self::Sub => "sub",
315 Self::And => "and",
316 Self::Nand => "nand",
317 Self::Or => "or",
318 Self::Xor => "xor",
319 Self::SMax => "smax",
320 Self::SMin => "smin",
321 Self::UMax => "umax",
322 Self::UMin => "umin",
323 Self::FAdd => "fadd",
324 Self::FSub => "fsub",
325 }
326 }
327
328 #[must_use]
330 pub fn from_name(name: &str) -> Option<Self> {
331 Self::all().find(|op| op.name() == name)
332 }
333
334 pub fn all() -> impl Iterator<Item = Self> {
336 [
337 Self::Xchg,
338 Self::Add,
339 Self::Sub,
340 Self::And,
341 Self::Nand,
342 Self::Or,
343 Self::Xor,
344 Self::SMax,
345 Self::SMin,
346 Self::UMax,
347 Self::UMin,
348 Self::FAdd,
349 Self::FSub,
350 ]
351 .into_iter()
352 }
353
354 #[must_use]
356 pub const fn is_float(self) -> bool {
357 matches!(self, Self::FAdd | Self::FSub)
358 }
359}
360
361impl fmt::Display for RmwOp {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 f.write_str(self.name())
364 }
365}
366
367#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
375pub enum StorageClass {
376 Static,
378 Automatic,
380 Allocated,
382 Mapped,
384 Mmio,
386 Device,
388 Function,
390 Literal,
392}
393
394impl StorageClass {
395 #[must_use]
397 pub const fn name(self) -> &'static str {
398 match self {
399 Self::Static => "static",
400 Self::Automatic => "automatic",
401 Self::Allocated => "allocated",
402 Self::Mapped => "mapped",
403 Self::Mmio => "mmio",
404 Self::Device => "device",
405 Self::Function => "function",
406 Self::Literal => "literal",
407 }
408 }
409
410 #[must_use]
412 pub fn from_name(name: &str) -> Option<Self> {
413 Self::all().find(|class| class.name() == name)
414 }
415
416 pub fn all() -> impl Iterator<Item = Self> {
418 [
419 Self::Static,
420 Self::Automatic,
421 Self::Allocated,
422 Self::Mapped,
423 Self::Mmio,
424 Self::Device,
425 Self::Function,
426 Self::Literal,
427 ]
428 .into_iter()
429 }
430}
431
432impl fmt::Display for StorageClass {
433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434 f.write_str(self.name())
435 }
436}
437
438#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
445pub enum Owner {
446 Device,
448 Uninstrumented,
450 Kernel,
452}
453
454impl Owner {
455 #[must_use]
457 pub const fn name(self) -> &'static str {
458 match self {
459 Self::Device => "device",
460 Self::Uninstrumented => "uninstrumented",
461 Self::Kernel => "kernel",
462 }
463 }
464
465 #[must_use]
467 pub fn from_name(name: &str) -> Option<Self> {
468 Self::all().find(|owner| owner.name() == name)
469 }
470
471 pub fn all() -> impl Iterator<Item = Self> {
473 [Self::Device, Self::Uninstrumented, Self::Kernel].into_iter()
474 }
475}
476
477impl fmt::Display for Owner {
478 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
479 f.write_str(self.name())
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486
487 #[test]
488 fn a_flag_set_is_two_bytes() {
489 assert_eq!(size_of::<Flags>(), 2);
490 }
491
492 #[test]
493 fn every_flag_has_a_name_and_finds_it_again() {
494 for &(flag, name) in NAMED {
495 assert_eq!(Flags::from_name(name), Some(flag), "{name}");
496 assert_eq!(flag.to_string(), format!(".{name}"));
497 }
498 assert_eq!(Flags::from_name("poison"), None);
499 assert_eq!(Flags::from_name(""), None);
500 }
501
502 #[test]
503 fn no_two_flags_share_a_bit() {
504 let mut seen = 0u16;
505 for &(flag, name) in NAMED {
506 assert_eq!(flag.bits().count_ones(), 1, "{name} is not one bit");
507 assert_eq!(seen & flag.bits(), 0, "{name} shares a bit");
508 seen |= flag.bits();
509 }
510 }
511
512 #[test]
513 fn fast_is_exactly_the_six_fast_math_flags() {
514 let named: Vec<&str> = Flags::FAST.iter().map(|(_, name)| name).collect();
515 assert_eq!(named, ["nnan", "ninf", "nsz", "arcp", "contract", "reassoc"]);
516 assert!(!Flags::FAST.contains(Flags::NSW));
517 assert!(!Flags::FAST.contains(Flags::VOLATILE));
518 }
519
520 #[test]
521 fn the_empty_set_prints_as_nothing() {
522 assert!(Flags::NONE.is_empty());
523 assert_eq!(Flags::NONE.to_string(), "");
524 assert_eq!(Flags::NONE.iter().count(), 0);
525 }
526
527 #[test]
528 fn flags_print_as_the_suffix_the_textual_form_uses() {
529 assert_eq!((Flags::NSW | Flags::NUW).to_string(), ".nsw.nuw");
530 assert_eq!((Flags::NUW | Flags::NSW).to_string(), ".nsw.nuw");
533 }
534
535 #[test]
536 fn intersecting_is_what_a_rewrite_keeps() {
537 let one = Flags::NSW | Flags::NUW;
538 let other = Flags::NSW;
539 assert_eq!(one.intersection(other), Flags::NSW);
540 assert_eq!(one.without(Flags::NSW), Flags::NUW);
541 assert!(one.contains(Flags::NSW));
542 assert!(!other.contains(Flags::NUW));
543 }
544
545 #[test]
546 fn wrapping_flags_go_on_arithmetic_and_nowhere_else() {
547 assert!(Flags::legal_on(Opcode::Add).contains(Flags::NSW));
548 assert!(Flags::legal_on(Opcode::Shl).contains(Flags::NUW));
549 assert!(!Flags::legal_on(Opcode::Add).contains(Flags::EXACT));
550 assert!(!Flags::legal_on(Opcode::FAdd).contains(Flags::NSW));
551 assert!(!Flags::legal_on(Opcode::Load).contains(Flags::NSW));
552 assert!(Flags::legal_on(Opcode::SDiv).contains(Flags::EXACT));
553 assert!(Flags::legal_on(Opcode::FMul).contains(Flags::CONTRACT));
554 assert!(Flags::legal_on(Opcode::Store).contains(Flags::VOLATILE));
555 assert!(Flags::legal_on(Opcode::Jump).is_empty());
556 }
557
558 #[test]
559 fn every_flag_is_legal_on_something() {
560 for &(flag, name) in NAMED {
561 assert!(
562 Opcode::all().any(|op| Flags::legal_on(op).contains(flag)),
563 "{name} is legal nowhere, so nothing can ever set it"
564 );
565 }
566 }
567
568 #[test]
569 fn a_load_cannot_release_and_a_store_cannot_acquire() {
570 assert!(MemOrder::Acquire.is_valid_for_load());
571 assert!(!MemOrder::Release.is_valid_for_load());
572 assert!(!MemOrder::AcqRel.is_valid_for_load());
573 assert!(MemOrder::Release.is_valid_for_store());
574 assert!(!MemOrder::Acquire.is_valid_for_store());
575 assert!(MemOrder::SeqCst.is_valid_for_load());
576 assert!(MemOrder::SeqCst.is_valid_for_store());
577 }
578
579 #[test]
580 fn not_atomic_is_valid_for_no_atomic_operation() {
581 assert!(!MemOrder::NotAtomic.is_valid_for_load());
582 assert!(!MemOrder::NotAtomic.is_valid_for_store());
583 assert!(!MemOrder::NotAtomic.is_valid_for_rmw());
584 assert_eq!(MemOrder::default(), MemOrder::NotAtomic);
585 }
586
587 #[test]
588 fn every_ordering_and_operation_finds_its_name_again() {
589 for order in MemOrder::all() {
590 assert_eq!(MemOrder::from_name(order.name()), Some(order));
591 }
592 for op in RmwOp::all() {
593 assert_eq!(RmwOp::from_name(op.name()), Some(op));
594 }
595 assert_eq!(MemOrder::from_name("consume"), None);
596 assert_eq!(RmwOp::from_name("fmul"), None);
597 }
598
599 #[test]
600 fn the_floating_read_modify_writes_are_the_two_that_have_one() {
601 let floats: Vec<&str> = RmwOp::all().filter(|op| op.is_float()).map(RmwOp::name).collect();
602 assert_eq!(floats, ["fadd", "fsub"]);
603 }
604
605 #[test]
606 fn every_storage_class_and_owner_finds_its_name_again() {
607 for class in StorageClass::all() {
608 assert_eq!(StorageClass::from_name(class.name()), Some(class));
609 }
610 for owner in Owner::all() {
611 assert_eq!(Owner::from_name(owner.name()), Some(owner));
612 }
613 assert_eq!(StorageClass::all().count(), 8);
616 assert_eq!(StorageClass::from_name("heap"), None);
617 assert_eq!(Owner::from_name("hardware"), None);
618 }
619}