1use std::fmt;
28
29use crate::Opcode;
30
31#[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
36pub struct Flags(u16);
37
38impl Flags {
39 pub const NONE: Self = Self(0);
41
42 pub const NSW: Self = Self(1 << 0);
45 pub const NUW: Self = Self(1 << 1);
48 pub const EXACT: Self = Self(1 << 2);
50
51 pub const NNAN: Self = Self(1 << 3);
53 pub const NINF: Self = Self(1 << 4);
55 pub const NSZ: Self = Self(1 << 5);
57 pub const ARCP: Self = Self(1 << 6);
59 pub const CONTRACT: Self = Self(1 << 7);
61 pub const REASSOC: Self = Self(1 << 8);
63
64 pub const VOLATILE: Self = Self(1 << 9);
66 pub const NOALIAS: Self = Self(1 << 10);
68
69 pub const NOFREE: Self = Self(1 << 11);
76
77 pub const FAST: Self = Self(
79 Self::NNAN.0
80 | Self::NINF.0
81 | Self::NSZ.0
82 | Self::ARCP.0
83 | Self::CONTRACT.0
84 | Self::REASSOC.0,
85 );
86
87 #[must_use]
89 pub const fn bits(self) -> u16 {
90 self.0
91 }
92
93 #[must_use]
95 pub const fn is_empty(self) -> bool {
96 self.0 == 0
97 }
98
99 #[must_use]
101 pub const fn contains(self, other: Self) -> bool {
102 self.0 & other.0 == other.0
103 }
104
105 #[must_use]
107 pub const fn union(self, other: Self) -> Self {
108 Self(self.0 | other.0)
109 }
110
111 #[must_use]
116 pub const fn intersection(self, other: Self) -> Self {
117 Self(self.0 & other.0)
118 }
119
120 #[must_use]
122 pub const fn without(self, other: Self) -> Self {
123 Self(self.0 & !other.0)
124 }
125
126 #[must_use]
132 pub const fn legal_on(opcode: Opcode) -> Self {
133 match opcode {
134 Opcode::Add | Opcode::Sub | Opcode::Mul | Opcode::Shl => Self::NSW.union(Self::NUW),
135 Opcode::SDiv | Opcode::UDiv | Opcode::LShr | Opcode::AShr => Self::EXACT,
136 Opcode::FAdd
137 | Opcode::FSub
138 | Opcode::FMul
139 | Opcode::FDiv
140 | Opcode::FRem
141 | Opcode::FNeg
142 | Opcode::Fma
143 | Opcode::FCmp => Self::FAST,
144 Opcode::Load | Opcode::Store | Opcode::Memcpy | Opcode::Memmove | Opcode::Memset => {
145 Self::VOLATILE
146 }
147 Opcode::InlineAsm => Self::VOLATILE,
148 Opcode::Call | Opcode::TailCall | Opcode::CallIndirect => Self::NOFREE,
153 Opcode::Alloca | Opcode::PtrAdd => Self::NOALIAS,
154 _ => Self::NONE,
155 }
156 }
157
158 pub fn iter(self) -> impl Iterator<Item = (Self, &'static str)> {
160 NAMED.iter().copied().filter(move |&(flag, _)| self.contains(flag))
161 }
162
163 #[must_use]
165 pub fn from_name(name: &str) -> Option<Self> {
166 NAMED.iter().find(|&&(_, named)| named == name).map(|&(flag, _)| flag)
167 }
168}
169
170impl std::ops::BitOr for Flags {
171 type Output = Self;
172
173 fn bitor(self, other: Self) -> Self {
174 self.union(other)
175 }
176}
177
178impl std::ops::BitOrAssign for Flags {
179 fn bitor_assign(&mut self, other: Self) {
180 *self = self.union(other);
181 }
182}
183
184impl fmt::Display for Flags {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 for (_, name) in self.iter() {
189 write!(f, ".{name}")?;
190 }
191 Ok(())
192 }
193}
194
195impl fmt::Debug for Flags {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 if self.is_empty() {
198 return f.write_str("Flags::NONE");
199 }
200 fmt::Display::fmt(self, f)
201 }
202}
203
204static NAMED: &[(Flags, &str)] = &[
206 (Flags::NSW, "nsw"),
207 (Flags::NUW, "nuw"),
208 (Flags::EXACT, "exact"),
209 (Flags::NNAN, "nnan"),
210 (Flags::NINF, "ninf"),
211 (Flags::NSZ, "nsz"),
212 (Flags::ARCP, "arcp"),
213 (Flags::CONTRACT, "contract"),
214 (Flags::REASSOC, "reassoc"),
215 (Flags::VOLATILE, "volatile"),
216 (Flags::NOALIAS, "noalias"),
217 (Flags::NOFREE, "nofree"),
218];
219
220#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
225pub enum MemOrder {
226 #[default]
228 NotAtomic,
229 Relaxed,
231 Acquire,
233 Release,
235 AcqRel,
237 SeqCst,
239}
240
241impl MemOrder {
242 #[must_use]
244 pub const fn name(self) -> &'static str {
245 match self {
246 Self::NotAtomic => "not_atomic",
247 Self::Relaxed => "relaxed",
248 Self::Acquire => "acquire",
249 Self::Release => "release",
250 Self::AcqRel => "acq_rel",
251 Self::SeqCst => "seq_cst",
252 }
253 }
254
255 #[must_use]
257 pub fn from_name(name: &str) -> Option<Self> {
258 Self::all().find(|order| order.name() == name)
259 }
260
261 pub fn all() -> impl Iterator<Item = Self> {
263 [Self::NotAtomic, Self::Relaxed, Self::Acquire, Self::Release, Self::AcqRel, Self::SeqCst]
264 .into_iter()
265 }
266
267 #[must_use]
271 pub const fn is_valid_for_load(self) -> bool {
272 matches!(self, Self::Relaxed | Self::Acquire | Self::SeqCst)
273 }
274
275 #[must_use]
279 pub const fn is_valid_for_store(self) -> bool {
280 matches!(self, Self::Relaxed | Self::Release | Self::SeqCst)
281 }
282
283 #[must_use]
285 pub const fn is_valid_for_rmw(self) -> bool {
286 !matches!(self, Self::NotAtomic)
287 }
288}
289
290impl fmt::Display for MemOrder {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 f.write_str(self.name())
293 }
294}
295
296#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
298pub enum RmwOp {
299 Xchg,
301 Add,
303 Sub,
305 And,
307 Nand,
309 Or,
311 Xor,
313 SMax,
315 SMin,
317 UMax,
319 UMin,
321 FAdd,
323 FSub,
325}
326
327impl RmwOp {
328 #[must_use]
330 pub const fn name(self) -> &'static str {
331 match self {
332 Self::Xchg => "xchg",
333 Self::Add => "add",
334 Self::Sub => "sub",
335 Self::And => "and",
336 Self::Nand => "nand",
337 Self::Or => "or",
338 Self::Xor => "xor",
339 Self::SMax => "smax",
340 Self::SMin => "smin",
341 Self::UMax => "umax",
342 Self::UMin => "umin",
343 Self::FAdd => "fadd",
344 Self::FSub => "fsub",
345 }
346 }
347
348 #[must_use]
350 pub fn from_name(name: &str) -> Option<Self> {
351 Self::all().find(|op| op.name() == name)
352 }
353
354 pub fn all() -> impl Iterator<Item = Self> {
356 [
357 Self::Xchg,
358 Self::Add,
359 Self::Sub,
360 Self::And,
361 Self::Nand,
362 Self::Or,
363 Self::Xor,
364 Self::SMax,
365 Self::SMin,
366 Self::UMax,
367 Self::UMin,
368 Self::FAdd,
369 Self::FSub,
370 ]
371 .into_iter()
372 }
373
374 #[must_use]
376 pub const fn is_float(self) -> bool {
377 matches!(self, Self::FAdd | Self::FSub)
378 }
379}
380
381impl fmt::Display for RmwOp {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 f.write_str(self.name())
384 }
385}
386
387#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
395pub enum StorageClass {
396 Static,
398 Automatic,
400 Allocated,
402 Mapped,
404 Mmio,
406 Device,
408 Function,
410 Literal,
412}
413
414impl StorageClass {
415 #[must_use]
417 pub const fn name(self) -> &'static str {
418 match self {
419 Self::Static => "static",
420 Self::Automatic => "automatic",
421 Self::Allocated => "allocated",
422 Self::Mapped => "mapped",
423 Self::Mmio => "mmio",
424 Self::Device => "device",
425 Self::Function => "function",
426 Self::Literal => "literal",
427 }
428 }
429
430 #[must_use]
432 pub fn from_name(name: &str) -> Option<Self> {
433 Self::all().find(|class| class.name() == name)
434 }
435
436 pub fn all() -> impl Iterator<Item = Self> {
438 [
439 Self::Static,
440 Self::Automatic,
441 Self::Allocated,
442 Self::Mapped,
443 Self::Mmio,
444 Self::Device,
445 Self::Function,
446 Self::Literal,
447 ]
448 .into_iter()
449 }
450}
451
452impl fmt::Display for StorageClass {
453 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454 f.write_str(self.name())
455 }
456}
457
458#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
465pub enum Owner {
466 Device,
468 Uninstrumented,
470 Kernel,
472}
473
474impl Owner {
475 #[must_use]
477 pub const fn name(self) -> &'static str {
478 match self {
479 Self::Device => "device",
480 Self::Uninstrumented => "uninstrumented",
481 Self::Kernel => "kernel",
482 }
483 }
484
485 #[must_use]
487 pub fn from_name(name: &str) -> Option<Self> {
488 Self::all().find(|owner| owner.name() == name)
489 }
490
491 pub fn all() -> impl Iterator<Item = Self> {
493 [Self::Device, Self::Uninstrumented, Self::Kernel].into_iter()
494 }
495}
496
497impl fmt::Display for Owner {
498 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
499 f.write_str(self.name())
500 }
501}
502
503#[cfg(test)]
504mod tests {
505 use super::*;
506
507 #[test]
508 fn a_flag_set_is_two_bytes() {
509 assert_eq!(size_of::<Flags>(), 2);
510 }
511
512 #[test]
513 fn every_flag_has_a_name_and_finds_it_again() {
514 for &(flag, name) in NAMED {
515 assert_eq!(Flags::from_name(name), Some(flag), "{name}");
516 assert_eq!(flag.to_string(), format!(".{name}"));
517 }
518 assert_eq!(Flags::from_name("poison"), None);
519 assert_eq!(Flags::from_name(""), None);
520 }
521
522 #[test]
523 fn no_two_flags_share_a_bit() {
524 let mut seen = 0u16;
525 for &(flag, name) in NAMED {
526 assert_eq!(flag.bits().count_ones(), 1, "{name} is not one bit");
527 assert_eq!(seen & flag.bits(), 0, "{name} shares a bit");
528 seen |= flag.bits();
529 }
530 }
531
532 #[test]
533 fn fast_is_exactly_the_six_fast_math_flags() {
534 let named: Vec<&str> = Flags::FAST.iter().map(|(_, name)| name).collect();
535 assert_eq!(named, ["nnan", "ninf", "nsz", "arcp", "contract", "reassoc"]);
536 assert!(!Flags::FAST.contains(Flags::NSW));
537 assert!(!Flags::FAST.contains(Flags::VOLATILE));
538 }
539
540 #[test]
541 fn the_empty_set_prints_as_nothing() {
542 assert!(Flags::NONE.is_empty());
543 assert_eq!(Flags::NONE.to_string(), "");
544 assert_eq!(Flags::NONE.iter().count(), 0);
545 }
546
547 #[test]
548 fn flags_print_as_the_suffix_the_textual_form_uses() {
549 assert_eq!((Flags::NSW | Flags::NUW).to_string(), ".nsw.nuw");
550 assert_eq!((Flags::NUW | Flags::NSW).to_string(), ".nsw.nuw");
553 }
554
555 #[test]
556 fn intersecting_is_what_a_rewrite_keeps() {
557 let one = Flags::NSW | Flags::NUW;
558 let other = Flags::NSW;
559 assert_eq!(one.intersection(other), Flags::NSW);
560 assert_eq!(one.without(Flags::NSW), Flags::NUW);
561 assert!(one.contains(Flags::NSW));
562 assert!(!other.contains(Flags::NUW));
563 }
564
565 #[test]
566 fn wrapping_flags_go_on_arithmetic_and_nowhere_else() {
567 assert!(Flags::legal_on(Opcode::Add).contains(Flags::NSW));
568 assert!(Flags::legal_on(Opcode::Shl).contains(Flags::NUW));
569 assert!(!Flags::legal_on(Opcode::Add).contains(Flags::EXACT));
570 assert!(!Flags::legal_on(Opcode::FAdd).contains(Flags::NSW));
571 assert!(!Flags::legal_on(Opcode::Load).contains(Flags::NSW));
572 assert!(Flags::legal_on(Opcode::SDiv).contains(Flags::EXACT));
573 assert!(Flags::legal_on(Opcode::FMul).contains(Flags::CONTRACT));
574 assert!(Flags::legal_on(Opcode::Store).contains(Flags::VOLATILE));
575 assert!(Flags::legal_on(Opcode::Jump).is_empty());
576 }
577
578 #[test]
579 fn nofree_goes_on_a_call_and_nowhere_else() {
580 for opcode in [Opcode::Call, Opcode::TailCall, Opcode::CallIndirect] {
581 assert!(Flags::legal_on(opcode).contains(Flags::NOFREE), "{opcode}");
582 }
583 for opcode in Opcode::all() {
584 let call = matches!(opcode, Opcode::Call | Opcode::TailCall | Opcode::CallIndirect);
585 assert_eq!(Flags::legal_on(opcode).contains(Flags::NOFREE), call, "{opcode}");
586 }
587 assert!(!Flags::FAST.contains(Flags::NOFREE));
590 }
591
592 #[test]
593 fn every_flag_is_legal_on_something() {
594 for &(flag, name) in NAMED {
595 assert!(
596 Opcode::all().any(|op| Flags::legal_on(op).contains(flag)),
597 "{name} is legal nowhere, so nothing can ever set it"
598 );
599 }
600 }
601
602 #[test]
603 fn a_load_cannot_release_and_a_store_cannot_acquire() {
604 assert!(MemOrder::Acquire.is_valid_for_load());
605 assert!(!MemOrder::Release.is_valid_for_load());
606 assert!(!MemOrder::AcqRel.is_valid_for_load());
607 assert!(MemOrder::Release.is_valid_for_store());
608 assert!(!MemOrder::Acquire.is_valid_for_store());
609 assert!(MemOrder::SeqCst.is_valid_for_load());
610 assert!(MemOrder::SeqCst.is_valid_for_store());
611 }
612
613 #[test]
614 fn not_atomic_is_valid_for_no_atomic_operation() {
615 assert!(!MemOrder::NotAtomic.is_valid_for_load());
616 assert!(!MemOrder::NotAtomic.is_valid_for_store());
617 assert!(!MemOrder::NotAtomic.is_valid_for_rmw());
618 assert_eq!(MemOrder::default(), MemOrder::NotAtomic);
619 }
620
621 #[test]
622 fn every_ordering_and_operation_finds_its_name_again() {
623 for order in MemOrder::all() {
624 assert_eq!(MemOrder::from_name(order.name()), Some(order));
625 }
626 for op in RmwOp::all() {
627 assert_eq!(RmwOp::from_name(op.name()), Some(op));
628 }
629 assert_eq!(MemOrder::from_name("consume"), None);
630 assert_eq!(RmwOp::from_name("fmul"), None);
631 }
632
633 #[test]
634 fn the_floating_read_modify_writes_are_the_two_that_have_one() {
635 let floats: Vec<&str> = RmwOp::all().filter(|op| op.is_float()).map(RmwOp::name).collect();
636 assert_eq!(floats, ["fadd", "fsub"]);
637 }
638
639 #[test]
640 fn every_storage_class_and_owner_finds_its_name_again() {
641 for class in StorageClass::all() {
642 assert_eq!(StorageClass::from_name(class.name()), Some(class));
643 }
644 for owner in Owner::all() {
645 assert_eq!(Owner::from_name(owner.name()), Some(owner));
646 }
647 assert_eq!(StorageClass::all().count(), 8);
650 assert_eq!(StorageClass::from_name("heap"), None);
651 assert_eq!(Owner::from_name("hardware"), None);
652 }
653}