cranelift_assembler_x64_meta/dsl/
features.rs

1//! A DSL for describing x64 CPU features.
2
3use core::fmt;
4use std::ops::BitOr;
5
6/// A collection of CPU features.
7///
8/// An instruction is valid when _any_ of the features in the collection are
9/// enabled; i.e., the collection is an `OR` expression.
10///
11/// ```
12/// # use cranelift_assembler_x64_meta::dsl::{Features, Feature};
13/// let fs = Feature::_64b | Feature::compat;
14/// assert_eq!(fs.to_string(), "_64b | compat");
15/// ```
16///
17/// Duplicate features are not allowed and will cause a panic.
18///
19/// ```should_panic
20/// # use cranelift_assembler_x64_meta::dsl::Feature;
21/// let fs = Feature::_64b | Feature::_64b;
22/// ```
23#[derive(PartialEq)]
24pub struct Features(Vec<Feature>);
25
26impl Features {
27    #[must_use]
28    pub fn is_empty(&self) -> bool {
29        self.0.is_empty()
30    }
31
32    pub fn iter(&self) -> impl Iterator<Item = &Feature> {
33        self.0.iter()
34    }
35}
36
37impl fmt::Display for Features {
38    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39        write!(
40            f,
41            "{}",
42            self.0
43                .iter()
44                .map(ToString::to_string)
45                .collect::<Vec<_>>()
46                .join(" | ")
47        )
48    }
49}
50
51/// A CPU feature.
52///
53/// IA-32e mode is the typical mode of operation for modern 64-bit x86
54/// processors. It consists of two sub-modes:
55/// - __64-bit mode__: uses the full 64-bit address space
56/// - __compatibility mode__: allows use of legacy 32-bit code
57///
58/// Other features listed here should match the __CPUID Feature Flags__ column
59/// of the instruction tables of the x64 reference manual.
60#[derive(Clone, Copy, Debug, PartialEq)]
61#[allow(non_camel_case_types, reason = "makes DSL definitions easier to read")]
62pub enum Feature {
63    _64b,
64    compat,
65    sse,
66    sse2,
67    ssse3,
68}
69
70/// List all CPU features.
71///
72/// It is critical that this list contains _all_ variants of the [`Feature`]
73/// `enum`. We use this list here in the `meta` level so that we can accurately
74/// transcribe each variant to an `enum` available in the generated layer above.
75/// If this list is incomplete, we will (fortunately) see compile errors for
76/// generated functions that use the missing variants.
77pub const ALL_FEATURES: &[Feature] = &[
78    Feature::_64b,
79    Feature::compat,
80    Feature::sse,
81    Feature::sse2,
82    Feature::ssse3,
83];
84
85impl fmt::Display for Feature {
86    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87        match self {
88            Feature::_64b => write!(f, "_64b"),
89            Feature::compat => write!(f, "compat"),
90            Feature::sse => write!(f, "sse"),
91            Feature::sse2 => write!(f, "sse2"),
92            Feature::ssse3 => write!(f, "ssse3"),
93        }
94    }
95}
96
97impl From<Feature> for Features {
98    fn from(flag: Feature) -> Self {
99        Features(vec![flag])
100    }
101}
102
103impl From<Option<Feature>> for Features {
104    fn from(flag: Option<Feature>) -> Self {
105        Features(flag.into_iter().collect())
106    }
107}
108
109impl BitOr for Feature {
110    type Output = Features;
111    fn bitor(self, rhs: Self) -> Self::Output {
112        assert_ne!(self, rhs, "duplicate feature: {self:?}");
113        Features(vec![self, rhs])
114    }
115}
116
117impl BitOr<Feature> for Features {
118    type Output = Features;
119    fn bitor(mut self, rhs: Feature) -> Self::Output {
120        assert!(!self.0.contains(&rhs), "duplicate feature: {rhs:?}");
121        self.0.push(rhs);
122        self
123    }
124}