near_vm_compiler/
target.rs

1// cspell:ignore cpuid, enumset, LZCNT, POPCNT, SSSE
2
3//! Target configuration
4use crate::error::ParseCpuFeatureError;
5use crate::lib::std::str::FromStr;
6use crate::lib::std::string::{String, ToString};
7use enumset::{EnumSet, EnumSetType};
8pub use target_lexicon::{
9    Architecture, BinaryFormat, CallingConvention, Endianness, OperatingSystem, PointerWidth,
10    Triple,
11};
12
13/// The nomenclature is inspired by the [`cpuid` crate].
14/// The list of supported features was initially retrieved from
15/// [`cranelift-native`].
16///
17/// The `CpuFeature` enum values are likely to grow closer to the
18/// original `cpuid`. However, we prefer to start small and grow from there.
19///
20/// If you would like to use a flag that doesn't exist yet here, please
21/// open a PR.
22///
23/// [`cpuid` crate]: https://docs.rs/cpuid/0.1.1/cpuid/enum.CpuFeature.html
24/// [`cranelift-native`]: https://github.com/bytecodealliance/cranelift/blob/6988545fd20249b084c53f4761b8c861266f5d31/cranelift-native/src/lib.rs#L51-L92
25#[allow(missing_docs, clippy::derived_hash_with_manual_eq)]
26#[derive(EnumSetType, Debug, Hash)]
27pub enum CpuFeature {
28    // X86 features
29    SSE2,
30    SSE3,
31    SSSE3,
32    SSE41,
33    SSE42,
34    POPCNT,
35    AVX,
36    BMI1,
37    BMI2,
38    AVX2,
39    AVX512DQ,
40    AVX512VL,
41    AVX512F,
42    LZCNT,
43    // ARM features
44    // Risc-V features
45}
46
47impl CpuFeature {
48    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
49    /// Retrieves the features for the current Host
50    pub fn for_host() -> EnumSet<Self> {
51        let mut features = EnumSet::new();
52
53        if std::is_x86_feature_detected!("sse2") {
54            features.insert(Self::SSE2);
55        }
56        if std::is_x86_feature_detected!("sse3") {
57            features.insert(Self::SSE3);
58        }
59        if std::is_x86_feature_detected!("ssse3") {
60            features.insert(Self::SSSE3);
61        }
62        if std::is_x86_feature_detected!("sse4.1") {
63            features.insert(Self::SSE41);
64        }
65        if std::is_x86_feature_detected!("sse4.2") {
66            features.insert(Self::SSE42);
67        }
68        if std::is_x86_feature_detected!("popcnt") {
69            features.insert(Self::POPCNT);
70        }
71        if std::is_x86_feature_detected!("avx") {
72            features.insert(Self::AVX);
73        }
74        if std::is_x86_feature_detected!("bmi1") {
75            features.insert(Self::BMI1);
76        }
77        if std::is_x86_feature_detected!("bmi2") {
78            features.insert(Self::BMI2);
79        }
80        if std::is_x86_feature_detected!("avx2") {
81            features.insert(Self::AVX2);
82        }
83        if std::is_x86_feature_detected!("avx512dq") {
84            features.insert(Self::AVX512DQ);
85        }
86        if std::is_x86_feature_detected!("avx512vl") {
87            features.insert(Self::AVX512VL);
88        }
89        if std::is_x86_feature_detected!("avx512f") {
90            features.insert(Self::AVX512F);
91        }
92        if std::is_x86_feature_detected!("lzcnt") {
93            features.insert(Self::LZCNT);
94        }
95        features
96    }
97    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
98    /// Retrieves the features for the current Host
99    pub fn for_host() -> EnumSet<Self> {
100        // We default to an empty hash set
101        EnumSet::new()
102    }
103
104    /// Retrieves an empty set of `CpuFeature`s.
105    pub fn set() -> EnumSet<Self> {
106        // We default to an empty hash set
107        EnumSet::new()
108    }
109}
110
111// This options should map exactly the GCC options indicated
112// here by architectures:
113//
114// X86: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
115// ARM: https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html
116// Aarch64: https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html
117impl FromStr for CpuFeature {
118    type Err = ParseCpuFeatureError;
119
120    fn from_str(s: &str) -> Result<Self, Self::Err> {
121        match s {
122            "sse2" => Ok(Self::SSE2),
123            "sse3" => Ok(Self::SSE3),
124            "ssse3" => Ok(Self::SSSE3),
125            "sse4.1" => Ok(Self::SSE41),
126            "sse4.2" => Ok(Self::SSE42),
127            "popcnt" => Ok(Self::POPCNT),
128            "avx" => Ok(Self::AVX),
129            "bmi" => Ok(Self::BMI1),
130            "bmi2" => Ok(Self::BMI2),
131            "avx2" => Ok(Self::AVX2),
132            "avx512dq" => Ok(Self::AVX512DQ),
133            "avx512vl" => Ok(Self::AVX512VL),
134            "avx512f" => Ok(Self::AVX512F),
135            "lzcnt" => Ok(Self::LZCNT),
136            _ => Err(ParseCpuFeatureError::Missing(s.to_string())),
137        }
138    }
139}
140
141impl ToString for CpuFeature {
142    fn to_string(&self) -> String {
143        match self {
144            Self::SSE2 => "sse2",
145            Self::SSE3 => "sse3",
146            Self::SSSE3 => "ssse3",
147            Self::SSE41 => "sse4.1",
148            Self::SSE42 => "sse4.2",
149            Self::POPCNT => "popcnt",
150            Self::AVX => "avx",
151            Self::BMI1 => "bmi",
152            Self::BMI2 => "bmi2",
153            Self::AVX2 => "avx2",
154            Self::AVX512DQ => "avx512dq",
155            Self::AVX512VL => "avx512vl",
156            Self::AVX512F => "avx512f",
157            Self::LZCNT => "lzcnt",
158        }
159        .to_string()
160    }
161}
162
163/// This is the target that we will use for compiling
164/// the WebAssembly ModuleInfo, and then run it.
165#[derive(Clone, Debug, PartialEq, Eq, Hash)]
166pub struct Target {
167    triple: Triple,
168    cpu_features: EnumSet<CpuFeature>,
169}
170
171impl Target {
172    /// Creates a new target given a triple
173    pub fn new(triple: Triple, cpu_features: EnumSet<CpuFeature>) -> Self {
174        Self { triple, cpu_features }
175    }
176
177    /// The triple associated for the target.
178    pub fn triple(&self) -> &Triple {
179        &self.triple
180    }
181
182    /// The triple associated for the target.
183    pub fn cpu_features(&self) -> &EnumSet<CpuFeature> {
184        &self.cpu_features
185    }
186}
187
188/// The default for the Target will use the HOST as the triple
189impl Default for Target {
190    fn default() -> Self {
191        Self { triple: Triple::host(), cpu_features: CpuFeature::for_host() }
192    }
193}