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        use Feature::*;
42        self.0
43            .iter()
44            .any(|f| matches!(f, sse | sse2 | sse3 | ssse3 | sse41 | sse42))
45    }
46}
47
48impl fmt::Display for Features {
49    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50        write!(
51            f,
52            "{}",
53            self.0
54                .iter()
55                .map(ToString::to_string)
56                .collect::<Vec<_>>()
57                .join(" | ")
58        )
59    }
60}
61
62/// A CPU feature.
63///
64/// IA-32e mode is the typical mode of operation for modern 64-bit x86
65/// processors. It consists of two sub-modes:
66/// - __64-bit mode__: uses the full 64-bit address space
67/// - __compatibility mode__: allows use of legacy 32-bit code
68///
69/// Other features listed here should match the __CPUID Feature Flags__ column
70/// of the instruction tables of the x64 reference manual.
71#[derive(Clone, Copy, Debug, PartialEq)]
72#[allow(non_camel_case_types, reason = "makes DSL definitions easier to read")]
73pub enum Feature {
74    _64b,
75    compat,
76    sse,
77    sse2,
78    sse3,
79    ssse3,
80    sse41,
81    sse42,
82    bmi1,
83    bmi2,
84    lzcnt,
85    popcnt,
86    avx,
87    avx2,
88    cmpxchg16b,
89    fma,
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::avx2,
114    Feature::cmpxchg16b,
115    Feature::fma,
116];
117
118impl fmt::Display for Feature {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        fmt::Debug::fmt(self, f)
121    }
122}
123
124impl From<Feature> for Features {
125    fn from(flag: Feature) -> Self {
126        Features(vec![flag])
127    }
128}
129
130impl From<Option<Feature>> for Features {
131    fn from(flag: Option<Feature>) -> Self {
132        Features(flag.into_iter().collect())
133    }
134}
135
136impl BitOr for Feature {
137    type Output = Features;
138    fn bitor(self, rhs: Self) -> Self::Output {
139        assert_ne!(self, rhs, "duplicate feature: {self:?}");
140        Features(vec![self, rhs])
141    }
142}
143
144impl BitOr<Feature> for Features {
145    type Output = Features;
146    fn bitor(mut self, rhs: Feature) -> Self::Output {
147        assert!(!self.0.contains(&rhs), "duplicate feature: {rhs:?}");
148        self.0.push(rhs);
149        self
150    }
151}