1use std::fmt;
29
30use crate::Opcode;
31
32#[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
37pub struct Flags(u16);
38
39impl Flags {
40 pub const NONE: Self = Self(0);
42
43 pub const NSW: Self = Self(1 << 0);
46 pub const NUW: Self = Self(1 << 1);
49 pub const EXACT: Self = Self(1 << 2);
51
52 pub const NNAN: Self = Self(1 << 3);
54 pub const NINF: Self = Self(1 << 4);
56 pub const NSZ: Self = Self(1 << 5);
58 pub const ARCP: Self = Self(1 << 6);
60 pub const CONTRACT: Self = Self(1 << 7);
62 pub const REASSOC: Self = Self(1 << 8);
64
65 pub const VOLATILE: Self = Self(1 << 9);
67 pub const NOALIAS: Self = Self(1 << 10);
69
70 pub const NOFREE: Self = Self(1 << 11);
77
78 pub const STATIC: Self = Self(1 << 12);
92
93 pub const FAST: Self = Self(
95 Self::NNAN.0
96 | Self::NINF.0
97 | Self::NSZ.0
98 | Self::ARCP.0
99 | Self::CONTRACT.0
100 | Self::REASSOC.0,
101 );
102
103 #[must_use]
105 pub const fn bits(self) -> u16 {
106 self.0
107 }
108
109 #[must_use]
111 pub const fn is_empty(self) -> bool {
112 self.0 == 0
113 }
114
115 #[must_use]
117 pub const fn contains(self, other: Self) -> bool {
118 self.0 & other.0 == other.0
119 }
120
121 #[must_use]
123 pub const fn union(self, other: Self) -> Self {
124 Self(self.0 | other.0)
125 }
126
127 #[must_use]
132 pub const fn intersection(self, other: Self) -> Self {
133 Self(self.0 & other.0)
134 }
135
136 #[must_use]
138 pub const fn without(self, other: Self) -> Self {
139 Self(self.0 & !other.0)
140 }
141
142 #[must_use]
148 pub const fn legal_on(opcode: Opcode) -> Self {
149 match opcode {
150 Opcode::Add | Opcode::Sub | Opcode::Mul | Opcode::Shl => Self::NSW.union(Self::NUW),
151 Opcode::SDiv | Opcode::UDiv | Opcode::LShr | Opcode::AShr => Self::EXACT,
152 Opcode::FAdd
153 | Opcode::FSub
154 | Opcode::FMul
155 | Opcode::FDiv
156 | Opcode::FRem
157 | Opcode::FNeg
158 | Opcode::Fma
159 | Opcode::FCmp => Self::FAST,
160 Opcode::Load | Opcode::Store | Opcode::Memcpy | Opcode::Memmove | Opcode::Memset => {
161 Self::VOLATILE
162 }
163 Opcode::AtomicLoad | Opcode::AtomicStore | Opcode::Cmpxchg | Opcode::AtomicRmw => {
168 Self::VOLATILE
169 }
170 Opcode::InlineAsm => Self::VOLATILE,
171 Opcode::Call | Opcode::TailCall | Opcode::CallIndirect => Self::NOFREE,
176 Opcode::CheckBounds | Opcode::CheckLive | Opcode::CheckDeriv => Self::STATIC,
179 Opcode::Alloca | Opcode::PtrAdd => Self::NOALIAS,
180 _ => Self::NONE,
181 }
182 }
183
184 pub fn iter(self) -> impl Iterator<Item = (Self, &'static str)> {
186 NAMED.iter().copied().filter(move |&(flag, _)| self.contains(flag))
187 }
188
189 #[must_use]
191 pub fn from_name(name: &str) -> Option<Self> {
192 NAMED.iter().find(|&&(_, named)| named == name).map(|&(flag, _)| flag)
193 }
194}
195
196impl std::ops::BitOr for Flags {
197 type Output = Self;
198
199 fn bitor(self, other: Self) -> Self {
200 self.union(other)
201 }
202}
203
204impl std::ops::BitOrAssign for Flags {
205 fn bitor_assign(&mut self, other: Self) {
206 *self = self.union(other);
207 }
208}
209
210impl fmt::Display for Flags {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 for (_, name) in self.iter() {
215 write!(f, ".{name}")?;
216 }
217 Ok(())
218 }
219}
220
221impl fmt::Debug for Flags {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 if self.is_empty() {
224 return f.write_str("Flags::NONE");
225 }
226 fmt::Display::fmt(self, f)
227 }
228}
229
230static NAMED: &[(Flags, &str)] = &[
232 (Flags::NSW, "nsw"),
233 (Flags::NUW, "nuw"),
234 (Flags::EXACT, "exact"),
235 (Flags::NNAN, "nnan"),
236 (Flags::NINF, "ninf"),
237 (Flags::NSZ, "nsz"),
238 (Flags::ARCP, "arcp"),
239 (Flags::CONTRACT, "contract"),
240 (Flags::REASSOC, "reassoc"),
241 (Flags::VOLATILE, "volatile"),
242 (Flags::NOALIAS, "noalias"),
243 (Flags::NOFREE, "nofree"),
244 (Flags::STATIC, "static"),
245];
246
247#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
252pub enum MemOrder {
253 #[default]
255 NotAtomic,
256 Relaxed,
258 Acquire,
260 Release,
262 AcqRel,
264 SeqCst,
266}
267
268impl MemOrder {
269 #[must_use]
271 pub const fn name(self) -> &'static str {
272 match self {
273 Self::NotAtomic => "not_atomic",
274 Self::Relaxed => "relaxed",
275 Self::Acquire => "acquire",
276 Self::Release => "release",
277 Self::AcqRel => "acq_rel",
278 Self::SeqCst => "seq_cst",
279 }
280 }
281
282 #[must_use]
284 pub fn from_name(name: &str) -> Option<Self> {
285 Self::all().find(|order| order.name() == name)
286 }
287
288 pub fn all() -> impl Iterator<Item = Self> {
290 [Self::NotAtomic, Self::Relaxed, Self::Acquire, Self::Release, Self::AcqRel, Self::SeqCst]
291 .into_iter()
292 }
293
294 #[must_use]
298 pub const fn is_valid_for_load(self) -> bool {
299 matches!(self, Self::Relaxed | Self::Acquire | Self::SeqCst)
300 }
301
302 #[must_use]
306 pub const fn is_valid_for_store(self) -> bool {
307 matches!(self, Self::Relaxed | Self::Release | Self::SeqCst)
308 }
309
310 #[must_use]
312 pub const fn is_valid_for_rmw(self) -> bool {
313 !matches!(self, Self::NotAtomic)
314 }
315}
316
317impl fmt::Display for MemOrder {
318 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319 f.write_str(self.name())
320 }
321}
322
323#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
325pub enum RmwOp {
326 Xchg,
328 Add,
330 Sub,
332 And,
334 Nand,
336 Or,
338 Xor,
340 SMax,
342 SMin,
344 UMax,
346 UMin,
348 FAdd,
350 FSub,
352}
353
354impl RmwOp {
355 #[must_use]
357 pub const fn name(self) -> &'static str {
358 match self {
359 Self::Xchg => "xchg",
360 Self::Add => "add",
361 Self::Sub => "sub",
362 Self::And => "and",
363 Self::Nand => "nand",
364 Self::Or => "or",
365 Self::Xor => "xor",
366 Self::SMax => "smax",
367 Self::SMin => "smin",
368 Self::UMax => "umax",
369 Self::UMin => "umin",
370 Self::FAdd => "fadd",
371 Self::FSub => "fsub",
372 }
373 }
374
375 #[must_use]
377 pub fn from_name(name: &str) -> Option<Self> {
378 Self::all().find(|op| op.name() == name)
379 }
380
381 pub fn all() -> impl Iterator<Item = Self> {
383 [
384 Self::Xchg,
385 Self::Add,
386 Self::Sub,
387 Self::And,
388 Self::Nand,
389 Self::Or,
390 Self::Xor,
391 Self::SMax,
392 Self::SMin,
393 Self::UMax,
394 Self::UMin,
395 Self::FAdd,
396 Self::FSub,
397 ]
398 .into_iter()
399 }
400
401 #[must_use]
403 pub const fn is_float(self) -> bool {
404 matches!(self, Self::FAdd | Self::FSub)
405 }
406}
407
408impl fmt::Display for RmwOp {
409 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410 f.write_str(self.name())
411 }
412}
413
414#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
422pub enum StorageClass {
423 Static,
425 Automatic,
427 Allocated,
429 Mapped,
431 Mmio,
433 Device,
435 Function,
437 Literal,
439}
440
441impl StorageClass {
442 #[must_use]
444 pub const fn name(self) -> &'static str {
445 match self {
446 Self::Static => "static",
447 Self::Automatic => "automatic",
448 Self::Allocated => "allocated",
449 Self::Mapped => "mapped",
450 Self::Mmio => "mmio",
451 Self::Device => "device",
452 Self::Function => "function",
453 Self::Literal => "literal",
454 }
455 }
456
457 #[must_use]
459 pub fn from_name(name: &str) -> Option<Self> {
460 Self::all().find(|class| class.name() == name)
461 }
462
463 pub fn all() -> impl Iterator<Item = Self> {
465 [
466 Self::Static,
467 Self::Automatic,
468 Self::Allocated,
469 Self::Mapped,
470 Self::Mmio,
471 Self::Device,
472 Self::Function,
473 Self::Literal,
474 ]
475 .into_iter()
476 }
477}
478
479impl fmt::Display for StorageClass {
480 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481 f.write_str(self.name())
482 }
483}
484
485#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
492pub enum Owner {
493 Device,
495 Uninstrumented,
497 Kernel,
499}
500
501impl Owner {
502 #[must_use]
504 pub const fn name(self) -> &'static str {
505 match self {
506 Self::Device => "device",
507 Self::Uninstrumented => "uninstrumented",
508 Self::Kernel => "kernel",
509 }
510 }
511
512 #[must_use]
514 pub fn from_name(name: &str) -> Option<Self> {
515 Self::all().find(|owner| owner.name() == name)
516 }
517
518 pub fn all() -> impl Iterator<Item = Self> {
520 [Self::Device, Self::Uninstrumented, Self::Kernel].into_iter()
521 }
522}
523
524impl fmt::Display for Owner {
525 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526 f.write_str(self.name())
527 }
528}
529
530#[cfg(test)]
531mod tests {
532 use super::*;
533
534 #[test]
535 fn a_flag_set_is_two_bytes() {
536 assert_eq!(size_of::<Flags>(), 2);
537 }
538
539 #[test]
540 fn every_flag_has_a_name_and_finds_it_again() {
541 for &(flag, name) in NAMED {
542 assert_eq!(Flags::from_name(name), Some(flag), "{name}");
543 assert_eq!(flag.to_string(), format!(".{name}"));
544 }
545 assert_eq!(Flags::from_name("poison"), None);
546 assert_eq!(Flags::from_name(""), None);
547 }
548
549 #[test]
550 fn no_two_flags_share_a_bit() {
551 let mut seen = 0u16;
552 for &(flag, name) in NAMED {
553 assert_eq!(flag.bits().count_ones(), 1, "{name} is not one bit");
554 assert_eq!(seen & flag.bits(), 0, "{name} shares a bit");
555 seen |= flag.bits();
556 }
557 }
558
559 #[test]
560 fn fast_is_exactly_the_six_fast_math_flags() {
561 let named: Vec<&str> = Flags::FAST.iter().map(|(_, name)| name).collect();
562 assert_eq!(named, ["nnan", "ninf", "nsz", "arcp", "contract", "reassoc"]);
563 assert!(!Flags::FAST.contains(Flags::NSW));
564 assert!(!Flags::FAST.contains(Flags::VOLATILE));
565 }
566
567 #[test]
568 fn the_empty_set_prints_as_nothing() {
569 assert!(Flags::NONE.is_empty());
570 assert_eq!(Flags::NONE.to_string(), "");
571 assert_eq!(Flags::NONE.iter().count(), 0);
572 }
573
574 #[test]
575 fn flags_print_as_the_suffix_the_textual_form_uses() {
576 assert_eq!((Flags::NSW | Flags::NUW).to_string(), ".nsw.nuw");
577 assert_eq!((Flags::NUW | Flags::NSW).to_string(), ".nsw.nuw");
580 }
581
582 #[test]
583 fn intersecting_is_what_a_rewrite_keeps() {
584 let one = Flags::NSW | Flags::NUW;
585 let other = Flags::NSW;
586 assert_eq!(one.intersection(other), Flags::NSW);
587 assert_eq!(one.without(Flags::NSW), Flags::NUW);
588 assert!(one.contains(Flags::NSW));
589 assert!(!other.contains(Flags::NUW));
590 }
591
592 #[test]
593 fn wrapping_flags_go_on_arithmetic_and_nowhere_else() {
594 assert!(Flags::legal_on(Opcode::Add).contains(Flags::NSW));
595 assert!(Flags::legal_on(Opcode::Shl).contains(Flags::NUW));
596 assert!(!Flags::legal_on(Opcode::Add).contains(Flags::EXACT));
597 assert!(!Flags::legal_on(Opcode::FAdd).contains(Flags::NSW));
598 assert!(!Flags::legal_on(Opcode::Load).contains(Flags::NSW));
599 assert!(Flags::legal_on(Opcode::SDiv).contains(Flags::EXACT));
600 assert!(Flags::legal_on(Opcode::FMul).contains(Flags::CONTRACT));
601 assert!(Flags::legal_on(Opcode::Store).contains(Flags::VOLATILE));
602 assert!(Flags::legal_on(Opcode::Jump).is_empty());
603 }
604
605 #[test]
606 fn nofree_goes_on_a_call_and_nowhere_else() {
607 for opcode in [Opcode::Call, Opcode::TailCall, Opcode::CallIndirect] {
608 assert!(Flags::legal_on(opcode).contains(Flags::NOFREE), "{opcode}");
609 }
610 for opcode in Opcode::all() {
611 let call = matches!(opcode, Opcode::Call | Opcode::TailCall | Opcode::CallIndirect);
612 assert_eq!(Flags::legal_on(opcode).contains(Flags::NOFREE), call, "{opcode}");
613 }
614 assert!(!Flags::FAST.contains(Flags::NOFREE));
617 }
618
619 #[test]
620 fn static_goes_on_a_safety_check_and_nowhere_else() {
621 for opcode in [Opcode::CheckBounds, Opcode::CheckLive, Opcode::CheckDeriv] {
622 assert!(Flags::legal_on(opcode).contains(Flags::STATIC), "{opcode}");
623 }
624 for opcode in Opcode::all() {
625 let check =
626 matches!(opcode, Opcode::CheckBounds | Opcode::CheckLive | Opcode::CheckDeriv);
627 assert_eq!(Flags::legal_on(opcode).contains(Flags::STATIC), check, "{opcode}");
628 }
629 assert!(!Flags::legal_on(Opcode::Call).contains(Flags::STATIC));
632 assert!(!Flags::legal_on(Opcode::CheckBounds).contains(Flags::NOFREE));
633 }
634
635 #[test]
636 fn every_flag_is_legal_on_something() {
637 for &(flag, name) in NAMED {
638 assert!(
639 Opcode::all().any(|op| Flags::legal_on(op).contains(flag)),
640 "{name} is legal nowhere, so nothing can ever set it"
641 );
642 }
643 }
644
645 #[test]
646 fn a_load_cannot_release_and_a_store_cannot_acquire() {
647 assert!(MemOrder::Acquire.is_valid_for_load());
648 assert!(!MemOrder::Release.is_valid_for_load());
649 assert!(!MemOrder::AcqRel.is_valid_for_load());
650 assert!(MemOrder::Release.is_valid_for_store());
651 assert!(!MemOrder::Acquire.is_valid_for_store());
652 assert!(MemOrder::SeqCst.is_valid_for_load());
653 assert!(MemOrder::SeqCst.is_valid_for_store());
654 }
655
656 #[test]
657 fn not_atomic_is_valid_for_no_atomic_operation() {
658 assert!(!MemOrder::NotAtomic.is_valid_for_load());
659 assert!(!MemOrder::NotAtomic.is_valid_for_store());
660 assert!(!MemOrder::NotAtomic.is_valid_for_rmw());
661 assert_eq!(MemOrder::default(), MemOrder::NotAtomic);
662 }
663
664 #[test]
665 fn every_ordering_and_operation_finds_its_name_again() {
666 for order in MemOrder::all() {
667 assert_eq!(MemOrder::from_name(order.name()), Some(order));
668 }
669 for op in RmwOp::all() {
670 assert_eq!(RmwOp::from_name(op.name()), Some(op));
671 }
672 assert_eq!(MemOrder::from_name("consume"), None);
673 assert_eq!(RmwOp::from_name("fmul"), None);
674 }
675
676 #[test]
677 fn the_floating_read_modify_writes_are_the_two_that_have_one() {
678 let floats: Vec<&str> = RmwOp::all().filter(|op| op.is_float()).map(RmwOp::name).collect();
679 assert_eq!(floats, ["fadd", "fsub"]);
680 }
681
682 #[test]
683 fn every_storage_class_and_owner_finds_its_name_again() {
684 for class in StorageClass::all() {
685 assert_eq!(StorageClass::from_name(class.name()), Some(class));
686 }
687 for owner in Owner::all() {
688 assert_eq!(Owner::from_name(owner.name()), Some(owner));
689 }
690 assert_eq!(StorageClass::all().count(), 8);
693 assert_eq!(StorageClass::from_name("heap"), None);
694 assert_eq!(Owner::from_name("hardware"), None);
695 }
696}