Skip to main content

cranelift_assembler_x64_meta/dsl/
features.rs

1//! A DSL for describing x64 CPU features.
2
3use core::fmt;
4use std::ops::{BitAnd, BitOr};
5
6/// A boolean term of CPU features.
7///
8/// An instruction is valid when the boolean term (a recursive tree of `AND` and
9/// `OR` terms) is satisfied.
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#[derive(PartialEq)]
17pub enum Features {
18    And(Box<Features>, Box<Features>),
19    Or(Box<Features>, Box<Features>),
20    Feature(Feature),
21}
22
23impl Features {
24    pub(crate) fn is_sse(&self) -> bool {
25        use Feature::*;
26        match self {
27            Features::And(lhs, rhs) => lhs.is_sse() || rhs.is_sse(),
28            Features::Or(lhs, rhs) => lhs.is_sse() || rhs.is_sse(),
29            Features::Feature(feature) => {
30                matches!(feature, sse | sse2 | sse3 | ssse3 | sse41 | sse42)
31            }
32        }
33    }
34}
35
36impl fmt::Display for Features {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            Features::And(lhs, rhs) => write!(f, "({lhs} & {rhs})"),
40            Features::Or(lhs, rhs) => write!(f, "({lhs} | {rhs})"),
41            Features::Feature(feature) => write!(f, "{feature:#?}"),
42        }
43    }
44}
45
46impl<T> BitOr<T> for Features
47where
48    T: Into<Features>,
49{
50    type Output = Features;
51    fn bitor(self, rhs: T) -> Self::Output {
52        Features::Or(Box::new(self), Box::new(rhs.into()))
53    }
54}
55
56impl<T> BitAnd<T> for Features
57where
58    T: Into<Features>,
59{
60    type Output = Features;
61    fn bitand(self, rhs: T) -> Self::Output {
62        Features::And(Box::new(self), Box::new(rhs.into()))
63    }
64}
65
66/// A CPU feature.
67///
68/// IA-32e mode is the typical mode of operation for modern 64-bit x86
69/// processors. It consists of two sub-modes:
70/// - __64-bit mode__: uses the full 64-bit address space
71/// - __compatibility mode__: allows use of legacy 32-bit code
72///
73/// Other features listed here should match the __CPUID Feature Flags__ column
74/// of the instruction tables of the x64 reference manual.
75#[derive(Clone, Copy, Debug, PartialEq)]
76#[allow(non_camel_case_types, reason = "makes DSL definitions easier to read")]
77pub enum Feature {
78    _64b,
79    compat,
80    sse,
81    sse2,
82    sse3,
83    ssse3,
84    sse41,
85    sse42,
86    bmi1,
87    bmi2,
88    lzcnt,
89    popcnt,
90    avx,
91    avx2,
92    avx512f,
93    avx512vl,
94    avx512dq,
95    avx512bitalg,
96    avx512vbmi,
97    cmpxchg16b,
98    fma,
99    avx_vnni,
100    avx512vnni,
101    apx,
102}
103
104/// List all CPU features.
105///
106/// It is critical that this list contains _all_ variants of the [`Feature`]
107/// `enum`. We use this list here in the `meta` level so that we can accurately
108/// transcribe each variant to an `enum` available in the generated layer above.
109/// If this list is incomplete, we will (fortunately) see compile errors for
110/// generated functions that use the missing variants.
111pub const ALL_FEATURES: &[Feature] = &[
112    Feature::_64b,
113    Feature::compat,
114    Feature::sse,
115    Feature::sse2,
116    Feature::sse3,
117    Feature::ssse3,
118    Feature::sse41,
119    Feature::sse42,
120    Feature::bmi1,
121    Feature::bmi2,
122    Feature::lzcnt,
123    Feature::popcnt,
124    Feature::avx,
125    Feature::avx2,
126    Feature::avx512f,
127    Feature::avx512vl,
128    Feature::avx512dq,
129    Feature::avx512bitalg,
130    Feature::avx512vbmi,
131    Feature::cmpxchg16b,
132    Feature::fma,
133    Feature::avx_vnni,
134    Feature::avx512vnni,
135    Feature::apx,
136];
137
138impl fmt::Display for Feature {
139    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140        fmt::Debug::fmt(self, f)
141    }
142}
143
144impl From<Feature> for Features {
145    fn from(f: Feature) -> Self {
146        Features::Feature(f)
147    }
148}
149
150impl<T> BitAnd<T> for Feature
151where
152    T: Into<Features>,
153{
154    type Output = Features;
155    fn bitand(self, rhs: T) -> Self::Output {
156        Features::from(self) & rhs.into()
157    }
158}
159
160impl<T> BitOr<T> for Feature
161where
162    T: Into<Features>,
163{
164    type Output = Features;
165    fn bitor(self, rhs: T) -> Self::Output {
166        Features::from(self) | rhs.into()
167    }
168}