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::InlineAsm => Self::VOLATILE,
164 Opcode::Call | Opcode::TailCall | Opcode::CallIndirect => Self::NOFREE,
169 Opcode::CheckBounds | Opcode::CheckLive | Opcode::CheckDeriv => Self::STATIC,
172 Opcode::Alloca | Opcode::PtrAdd => Self::NOALIAS,
173 _ => Self::NONE,
174 }
175 }
176
177 pub fn iter(self) -> impl Iterator<Item = (Self, &'static str)> {
179 NAMED.iter().copied().filter(move |&(flag, _)| self.contains(flag))
180 }
181
182 #[must_use]
184 pub fn from_name(name: &str) -> Option<Self> {
185 NAMED.iter().find(|&&(_, named)| named == name).map(|&(flag, _)| flag)
186 }
187}
188
189impl std::ops::BitOr for Flags {
190 type Output = Self;
191
192 fn bitor(self, other: Self) -> Self {
193 self.union(other)
194 }
195}
196
197impl std::ops::BitOrAssign for Flags {
198 fn bitor_assign(&mut self, other: Self) {
199 *self = self.union(other);
200 }
201}
202
203impl fmt::Display for Flags {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 for (_, name) in self.iter() {
208 write!(f, ".{name}")?;
209 }
210 Ok(())
211 }
212}
213
214impl fmt::Debug for Flags {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 if self.is_empty() {
217 return f.write_str("Flags::NONE");
218 }
219 fmt::Display::fmt(self, f)
220 }
221}
222
223static NAMED: &[(Flags, &str)] = &[
225 (Flags::NSW, "nsw"),
226 (Flags::NUW, "nuw"),
227 (Flags::EXACT, "exact"),
228 (Flags::NNAN, "nnan"),
229 (Flags::NINF, "ninf"),
230 (Flags::NSZ, "nsz"),
231 (Flags::ARCP, "arcp"),
232 (Flags::CONTRACT, "contract"),
233 (Flags::REASSOC, "reassoc"),
234 (Flags::VOLATILE, "volatile"),
235 (Flags::NOALIAS, "noalias"),
236 (Flags::NOFREE, "nofree"),
237 (Flags::STATIC, "static"),
238];
239
240#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
245pub enum MemOrder {
246 #[default]
248 NotAtomic,
249 Relaxed,
251 Acquire,
253 Release,
255 AcqRel,
257 SeqCst,
259}
260
261impl MemOrder {
262 #[must_use]
264 pub const fn name(self) -> &'static str {
265 match self {
266 Self::NotAtomic => "not_atomic",
267 Self::Relaxed => "relaxed",
268 Self::Acquire => "acquire",
269 Self::Release => "release",
270 Self::AcqRel => "acq_rel",
271 Self::SeqCst => "seq_cst",
272 }
273 }
274
275 #[must_use]
277 pub fn from_name(name: &str) -> Option<Self> {
278 Self::all().find(|order| order.name() == name)
279 }
280
281 pub fn all() -> impl Iterator<Item = Self> {
283 [Self::NotAtomic, Self::Relaxed, Self::Acquire, Self::Release, Self::AcqRel, Self::SeqCst]
284 .into_iter()
285 }
286
287 #[must_use]
291 pub const fn is_valid_for_load(self) -> bool {
292 matches!(self, Self::Relaxed | Self::Acquire | Self::SeqCst)
293 }
294
295 #[must_use]
299 pub const fn is_valid_for_store(self) -> bool {
300 matches!(self, Self::Relaxed | Self::Release | Self::SeqCst)
301 }
302
303 #[must_use]
305 pub const fn is_valid_for_rmw(self) -> bool {
306 !matches!(self, Self::NotAtomic)
307 }
308}
309
310impl fmt::Display for MemOrder {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 f.write_str(self.name())
313 }
314}
315
316#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
318pub enum RmwOp {
319 Xchg,
321 Add,
323 Sub,
325 And,
327 Nand,
329 Or,
331 Xor,
333 SMax,
335 SMin,
337 UMax,
339 UMin,
341 FAdd,
343 FSub,
345}
346
347impl RmwOp {
348 #[must_use]
350 pub const fn name(self) -> &'static str {
351 match self {
352 Self::Xchg => "xchg",
353 Self::Add => "add",
354 Self::Sub => "sub",
355 Self::And => "and",
356 Self::Nand => "nand",
357 Self::Or => "or",
358 Self::Xor => "xor",
359 Self::SMax => "smax",
360 Self::SMin => "smin",
361 Self::UMax => "umax",
362 Self::UMin => "umin",
363 Self::FAdd => "fadd",
364 Self::FSub => "fsub",
365 }
366 }
367
368 #[must_use]
370 pub fn from_name(name: &str) -> Option<Self> {
371 Self::all().find(|op| op.name() == name)
372 }
373
374 pub fn all() -> impl Iterator<Item = Self> {
376 [
377 Self::Xchg,
378 Self::Add,
379 Self::Sub,
380 Self::And,
381 Self::Nand,
382 Self::Or,
383 Self::Xor,
384 Self::SMax,
385 Self::SMin,
386 Self::UMax,
387 Self::UMin,
388 Self::FAdd,
389 Self::FSub,
390 ]
391 .into_iter()
392 }
393
394 #[must_use]
396 pub const fn is_float(self) -> bool {
397 matches!(self, Self::FAdd | Self::FSub)
398 }
399}
400
401impl fmt::Display for RmwOp {
402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403 f.write_str(self.name())
404 }
405}
406
407#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
415pub enum StorageClass {
416 Static,
418 Automatic,
420 Allocated,
422 Mapped,
424 Mmio,
426 Device,
428 Function,
430 Literal,
432}
433
434impl StorageClass {
435 #[must_use]
437 pub const fn name(self) -> &'static str {
438 match self {
439 Self::Static => "static",
440 Self::Automatic => "automatic",
441 Self::Allocated => "allocated",
442 Self::Mapped => "mapped",
443 Self::Mmio => "mmio",
444 Self::Device => "device",
445 Self::Function => "function",
446 Self::Literal => "literal",
447 }
448 }
449
450 #[must_use]
452 pub fn from_name(name: &str) -> Option<Self> {
453 Self::all().find(|class| class.name() == name)
454 }
455
456 pub fn all() -> impl Iterator<Item = Self> {
458 [
459 Self::Static,
460 Self::Automatic,
461 Self::Allocated,
462 Self::Mapped,
463 Self::Mmio,
464 Self::Device,
465 Self::Function,
466 Self::Literal,
467 ]
468 .into_iter()
469 }
470}
471
472impl fmt::Display for StorageClass {
473 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474 f.write_str(self.name())
475 }
476}
477
478#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
485pub enum Owner {
486 Device,
488 Uninstrumented,
490 Kernel,
492}
493
494impl Owner {
495 #[must_use]
497 pub const fn name(self) -> &'static str {
498 match self {
499 Self::Device => "device",
500 Self::Uninstrumented => "uninstrumented",
501 Self::Kernel => "kernel",
502 }
503 }
504
505 #[must_use]
507 pub fn from_name(name: &str) -> Option<Self> {
508 Self::all().find(|owner| owner.name() == name)
509 }
510
511 pub fn all() -> impl Iterator<Item = Self> {
513 [Self::Device, Self::Uninstrumented, Self::Kernel].into_iter()
514 }
515}
516
517impl fmt::Display for Owner {
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 f.write_str(self.name())
520 }
521}
522
523#[cfg(test)]
524mod tests {
525 use super::*;
526
527 #[test]
528 fn a_flag_set_is_two_bytes() {
529 assert_eq!(size_of::<Flags>(), 2);
530 }
531
532 #[test]
533 fn every_flag_has_a_name_and_finds_it_again() {
534 for &(flag, name) in NAMED {
535 assert_eq!(Flags::from_name(name), Some(flag), "{name}");
536 assert_eq!(flag.to_string(), format!(".{name}"));
537 }
538 assert_eq!(Flags::from_name("poison"), None);
539 assert_eq!(Flags::from_name(""), None);
540 }
541
542 #[test]
543 fn no_two_flags_share_a_bit() {
544 let mut seen = 0u16;
545 for &(flag, name) in NAMED {
546 assert_eq!(flag.bits().count_ones(), 1, "{name} is not one bit");
547 assert_eq!(seen & flag.bits(), 0, "{name} shares a bit");
548 seen |= flag.bits();
549 }
550 }
551
552 #[test]
553 fn fast_is_exactly_the_six_fast_math_flags() {
554 let named: Vec<&str> = Flags::FAST.iter().map(|(_, name)| name).collect();
555 assert_eq!(named, ["nnan", "ninf", "nsz", "arcp", "contract", "reassoc"]);
556 assert!(!Flags::FAST.contains(Flags::NSW));
557 assert!(!Flags::FAST.contains(Flags::VOLATILE));
558 }
559
560 #[test]
561 fn the_empty_set_prints_as_nothing() {
562 assert!(Flags::NONE.is_empty());
563 assert_eq!(Flags::NONE.to_string(), "");
564 assert_eq!(Flags::NONE.iter().count(), 0);
565 }
566
567 #[test]
568 fn flags_print_as_the_suffix_the_textual_form_uses() {
569 assert_eq!((Flags::NSW | Flags::NUW).to_string(), ".nsw.nuw");
570 assert_eq!((Flags::NUW | Flags::NSW).to_string(), ".nsw.nuw");
573 }
574
575 #[test]
576 fn intersecting_is_what_a_rewrite_keeps() {
577 let one = Flags::NSW | Flags::NUW;
578 let other = Flags::NSW;
579 assert_eq!(one.intersection(other), Flags::NSW);
580 assert_eq!(one.without(Flags::NSW), Flags::NUW);
581 assert!(one.contains(Flags::NSW));
582 assert!(!other.contains(Flags::NUW));
583 }
584
585 #[test]
586 fn wrapping_flags_go_on_arithmetic_and_nowhere_else() {
587 assert!(Flags::legal_on(Opcode::Add).contains(Flags::NSW));
588 assert!(Flags::legal_on(Opcode::Shl).contains(Flags::NUW));
589 assert!(!Flags::legal_on(Opcode::Add).contains(Flags::EXACT));
590 assert!(!Flags::legal_on(Opcode::FAdd).contains(Flags::NSW));
591 assert!(!Flags::legal_on(Opcode::Load).contains(Flags::NSW));
592 assert!(Flags::legal_on(Opcode::SDiv).contains(Flags::EXACT));
593 assert!(Flags::legal_on(Opcode::FMul).contains(Flags::CONTRACT));
594 assert!(Flags::legal_on(Opcode::Store).contains(Flags::VOLATILE));
595 assert!(Flags::legal_on(Opcode::Jump).is_empty());
596 }
597
598 #[test]
599 fn nofree_goes_on_a_call_and_nowhere_else() {
600 for opcode in [Opcode::Call, Opcode::TailCall, Opcode::CallIndirect] {
601 assert!(Flags::legal_on(opcode).contains(Flags::NOFREE), "{opcode}");
602 }
603 for opcode in Opcode::all() {
604 let call = matches!(opcode, Opcode::Call | Opcode::TailCall | Opcode::CallIndirect);
605 assert_eq!(Flags::legal_on(opcode).contains(Flags::NOFREE), call, "{opcode}");
606 }
607 assert!(!Flags::FAST.contains(Flags::NOFREE));
610 }
611
612 #[test]
613 fn static_goes_on_a_safety_check_and_nowhere_else() {
614 for opcode in [Opcode::CheckBounds, Opcode::CheckLive, Opcode::CheckDeriv] {
615 assert!(Flags::legal_on(opcode).contains(Flags::STATIC), "{opcode}");
616 }
617 for opcode in Opcode::all() {
618 let check =
619 matches!(opcode, Opcode::CheckBounds | Opcode::CheckLive | Opcode::CheckDeriv);
620 assert_eq!(Flags::legal_on(opcode).contains(Flags::STATIC), check, "{opcode}");
621 }
622 assert!(!Flags::legal_on(Opcode::Call).contains(Flags::STATIC));
625 assert!(!Flags::legal_on(Opcode::CheckBounds).contains(Flags::NOFREE));
626 }
627
628 #[test]
629 fn every_flag_is_legal_on_something() {
630 for &(flag, name) in NAMED {
631 assert!(
632 Opcode::all().any(|op| Flags::legal_on(op).contains(flag)),
633 "{name} is legal nowhere, so nothing can ever set it"
634 );
635 }
636 }
637
638 #[test]
639 fn a_load_cannot_release_and_a_store_cannot_acquire() {
640 assert!(MemOrder::Acquire.is_valid_for_load());
641 assert!(!MemOrder::Release.is_valid_for_load());
642 assert!(!MemOrder::AcqRel.is_valid_for_load());
643 assert!(MemOrder::Release.is_valid_for_store());
644 assert!(!MemOrder::Acquire.is_valid_for_store());
645 assert!(MemOrder::SeqCst.is_valid_for_load());
646 assert!(MemOrder::SeqCst.is_valid_for_store());
647 }
648
649 #[test]
650 fn not_atomic_is_valid_for_no_atomic_operation() {
651 assert!(!MemOrder::NotAtomic.is_valid_for_load());
652 assert!(!MemOrder::NotAtomic.is_valid_for_store());
653 assert!(!MemOrder::NotAtomic.is_valid_for_rmw());
654 assert_eq!(MemOrder::default(), MemOrder::NotAtomic);
655 }
656
657 #[test]
658 fn every_ordering_and_operation_finds_its_name_again() {
659 for order in MemOrder::all() {
660 assert_eq!(MemOrder::from_name(order.name()), Some(order));
661 }
662 for op in RmwOp::all() {
663 assert_eq!(RmwOp::from_name(op.name()), Some(op));
664 }
665 assert_eq!(MemOrder::from_name("consume"), None);
666 assert_eq!(RmwOp::from_name("fmul"), None);
667 }
668
669 #[test]
670 fn the_floating_read_modify_writes_are_the_two_that_have_one() {
671 let floats: Vec<&str> = RmwOp::all().filter(|op| op.is_float()).map(RmwOp::name).collect();
672 assert_eq!(floats, ["fadd", "fsub"]);
673 }
674
675 #[test]
676 fn every_storage_class_and_owner_finds_its_name_again() {
677 for class in StorageClass::all() {
678 assert_eq!(StorageClass::from_name(class.name()), Some(class));
679 }
680 for owner in Owner::all() {
681 assert_eq!(Owner::from_name(owner.name()), Some(owner));
682 }
683 assert_eq!(StorageClass::all().count(), 8);
686 assert_eq!(StorageClass::from_name("heap"), None);
687 assert_eq!(Owner::from_name("hardware"), None);
688 }
689}