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    pub fn contains(&self, feature: Feature) -> bool {
37        self.0.contains(&feature)
38    }
39
40    pub(crate) fn is_sse(&self) -> bool {
41        self.0.iter().any(|f| {
42            matches!(
43                f,
44                Feature::sse | Feature::sse2 | Feature::ssse3 | Feature::sse41
45            )
46        })
47    }
48}
49
50impl fmt::Display for Features {
51    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52        write!(
53            f,
54            "{}",
55            self.0
56                .iter()
57                .map(ToString::to_string)
58                .collect::<Vec<_>>()
59                .join(" | ")
60        )
61    }
62}
63
64/// A CPU feature.
65///
66/// IA-32e mode is the typical mode of operation for modern 64-bit x86
67/// processors. It consists of two sub-modes:
68/// - __64-bit mode__: uses the full 64-bit address space
69/// - __compatibility mode__: allows use of legacy 32-bit code
70///
71/// Other features listed here should match the __CPUID Feature Flags__ column
72/// of the instruction tables of the x64 reference manual.
73#[derive(Clone, Copy, Debug, PartialEq)]
74#[allow(non_camel_case_types, reason = "makes DSL definitions easier to read")]
75pub enum Feature {
76    _64b,
77    compat,
78    sse,
79    sse2,
80    sse3,
81    ssse3,
82    sse41,
83    sse42,
84    bmi1,
85    bmi2,
86    lzcnt,
87    popcnt,
88    avx,
89    cmpxchg16b,
90}
91
92/// List all CPU features.
93///
94/// It is critical that this list contains _all_ variants of the [`Feature`]
95/// `enum`. We use this list here in the `meta` level so that we can accurately
96/// transcribe each variant to an `enum` available in the generated layer above.
97/// If this list is incomplete, we will (fortunately) see compile errors for
98/// generated functions that use the missing variants.
99pub const ALL_FEATURES: &[Feature] = &[
100    Feature::_64b,
101    Feature::compat,
102    Feature::sse,
103    Feature::sse2,
104    Feature::sse3,
105    Feature::ssse3,
106    Feature::sse41,
107    Feature::sse42,
108    Feature::bmi1,
109    Feature::bmi2,
110    Feature::lzcnt,
111    Feature::popcnt,
112    Feature::avx,
113    Feature::cmpxchg16b,
114];
115
116impl fmt::Display for Feature {
117    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118        fmt::Debug::fmt(self, f)
119    }
120}
121
122impl From<Feature> for Features {
123    fn from(flag: Feature) -> Self {
124        Features(vec![flag])
125    }
126}
127
128impl From<Option<Feature>> for Features {
129    fn from(flag: Option<Feature>) -> Self {
130        Features(flag.into_iter().collect())
131    }
132}
133
134impl BitOr for Feature {
135    type Output = Features;
136    fn bitor(self, rhs: Self) -> Self::Output {
137        assert_ne!(self, rhs, "duplicate feature: {self:?}");
138        Features(vec![self, rhs])
139    }
140}
141
142impl BitOr<Feature> for Features {
143    type Output = Features;
144    fn bitor(mut self, rhs: Feature) -> Self::Output {
145        assert!(!self.0.contains(&rhs), "duplicate feature: {rhs:?}");
146        self.0.push(rhs);
147        self
148    }
149}