1 //! A DSL for describing x64 CPU features. 2 3 use core::fmt; 4 use 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)] 17 pub enum Features { 18 And(Box<Features>, Box<Features>), 19 Or(Box<Features>, Box<Features>), 20 Feature(Feature), 21 } 22 23 impl Features { is_sse(&self) -> bool24 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 36 impl fmt::Display for Features { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result37 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 46 impl<T> BitOr<T> for Features 47 where 48 T: Into<Features>, 49 { 50 type Output = Features; bitor(self, rhs: T) -> Self::Output51 fn bitor(self, rhs: T) -> Self::Output { 52 Features::Or(Box::new(self), Box::new(rhs.into())) 53 } 54 } 55 56 impl<T> BitAnd<T> for Features 57 where 58 T: Into<Features>, 59 { 60 type Output = Features; bitand(self, rhs: T) -> Self::Output61 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")] 77 pub 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 } 100 101 /// List all CPU features. 102 /// 103 /// It is critical that this list contains _all_ variants of the [`Feature`] 104 /// `enum`. We use this list here in the `meta` level so that we can accurately 105 /// transcribe each variant to an `enum` available in the generated layer above. 106 /// If this list is incomplete, we will (fortunately) see compile errors for 107 /// generated functions that use the missing variants. 108 pub const ALL_FEATURES: &[Feature] = &[ 109 Feature::_64b, 110 Feature::compat, 111 Feature::sse, 112 Feature::sse2, 113 Feature::sse3, 114 Feature::ssse3, 115 Feature::sse41, 116 Feature::sse42, 117 Feature::bmi1, 118 Feature::bmi2, 119 Feature::lzcnt, 120 Feature::popcnt, 121 Feature::avx, 122 Feature::avx2, 123 Feature::avx512f, 124 Feature::avx512vl, 125 Feature::avx512dq, 126 Feature::avx512bitalg, 127 Feature::avx512vbmi, 128 Feature::cmpxchg16b, 129 Feature::fma, 130 ]; 131 132 impl fmt::Display for Feature { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result133 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 134 fmt::Debug::fmt(self, f) 135 } 136 } 137 138 impl From<Feature> for Features { from(f: Feature) -> Self139 fn from(f: Feature) -> Self { 140 Features::Feature(f) 141 } 142 } 143 144 impl<T> BitAnd<T> for Feature 145 where 146 T: Into<Features>, 147 { 148 type Output = Features; bitand(self, rhs: T) -> Self::Output149 fn bitand(self, rhs: T) -> Self::Output { 150 Features::from(self) & rhs.into() 151 } 152 } 153 154 impl<T> BitOr<T> for Feature 155 where 156 T: Into<Features>, 157 { 158 type Output = Features; bitor(self, rhs: T) -> Self::Output159 fn bitor(self, rhs: T) -> Self::Output { 160 Features::from(self) | rhs.into() 161 } 162 } 163