Skip to main content

asmkit/core/
target.rs

1pub use super::arch_traits::Arch;
2
3#[cfg(feature = "aarch64")]
4use crate::aarch64::CPU_FEATURE_COUNT as AARCH64_FEATURE_COUNT;
5#[cfg(feature = "aarch64")]
6pub use crate::aarch64::CpuFeature as AArch64Feature;
7#[cfg(feature = "riscv")]
8pub use crate::riscv::instdb::CpuFeature as RiscVFeature;
9#[cfg(feature = "riscv")]
10use crate::riscv::instdb::{
11    BASELINE_CPU_FEATURES as BASELINE_RISCV_FEATURES, CPU_FEATURE_WORDS as RISCV_FEATURE_WORDS,
12};
13#[cfg(feature = "x86")]
14pub use crate::x86::instdb::CpuFeature as X86Feature;
15#[cfg(feature = "x86")]
16use crate::x86::instdb::{CPU_FEATURE_COUNT, DEFAULT_X86_FEATURES};
17
18#[cfg(feature = "x86")]
19const X86_FEATURE_WORDS: usize = CPU_FEATURE_COUNT.div_ceil(64);
20
21#[cfg(feature = "x86")]
22const fn default_x86_features(arch: Arch) -> [u64; X86_FEATURE_WORDS] {
23    let mut features = [0; X86_FEATURE_WORDS];
24    if matches!(arch, Arch::X86 | Arch::X64) {
25        let mut index = 0;
26        while index < DEFAULT_X86_FEATURES.len() {
27            let feature = DEFAULT_X86_FEATURES[index] as usize;
28            features[feature / 64] |= 1u64 << (feature % 64);
29            index += 1;
30        }
31    }
32    features
33}
34
35#[cfg(feature = "aarch64")]
36const fn default_aarch64_features(arch: Arch) -> u64 {
37    if matches!(arch, Arch::AArch64) {
38        if AARCH64_FEATURE_COUNT == 64 {
39            u64::MAX
40        } else {
41            (1u64 << AARCH64_FEATURE_COUNT) - 1
42        }
43    } else {
44        0
45    }
46}
47
48#[cfg(feature = "riscv")]
49const fn default_riscv_features(arch: Arch) -> [u64; RISCV_FEATURE_WORDS] {
50    if matches!(arch, Arch::RISCV32 | Arch::RISCV64) {
51        [u64::MAX; RISCV_FEATURE_WORDS]
52    } else {
53        [0; RISCV_FEATURE_WORDS]
54    }
55}
56
57#[cfg(feature = "riscv")]
58const fn baseline_riscv_features(arch: Arch) -> [u64; RISCV_FEATURE_WORDS] {
59    let mut features = [0; RISCV_FEATURE_WORDS];
60    if matches!(arch, Arch::RISCV32 | Arch::RISCV64) {
61        let mut index = 0;
62        while index < BASELINE_RISCV_FEATURES.len() {
63            let feature = BASELINE_RISCV_FEATURES[index] as usize;
64            features[feature / 64] |= 1u64 << (feature % 64);
65            index += 1;
66        }
67    }
68    features
69}
70
71#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
72pub struct Environment {
73    arch: Arch,
74    is_pic: bool,
75    #[cfg(feature = "x86")]
76    x86_features: [u64; X86_FEATURE_WORDS],
77    #[cfg(feature = "aarch64")]
78    aarch64_features: u64,
79    #[cfg(feature = "riscv")]
80    riscv_features: [u64; RISCV_FEATURE_WORDS],
81}
82
83impl Default for Environment {
84    fn default() -> Self {
85        Self::host()
86    }
87}
88
89impl Environment {
90    pub const fn new(arch: Arch) -> Self {
91        Self {
92            arch,
93            is_pic: false,
94            #[cfg(feature = "x86")]
95            x86_features: default_x86_features(arch),
96            #[cfg(feature = "aarch64")]
97            aarch64_features: default_aarch64_features(arch),
98            #[cfg(feature = "riscv")]
99            riscv_features: default_riscv_features(arch),
100        }
101    }
102
103    /// Creates a target with no optional ISA extensions enabled.
104    pub const fn baseline(arch: Arch) -> Self {
105        Self {
106            arch,
107            is_pic: false,
108            #[cfg(feature = "x86")]
109            x86_features: [0; X86_FEATURE_WORDS],
110            #[cfg(feature = "aarch64")]
111            aarch64_features: 0,
112            #[cfg(feature = "riscv")]
113            riscv_features: baseline_riscv_features(arch),
114        }
115    }
116
117    pub const fn host() -> Self {
118        Self {
119            arch: Arch::HOST,
120            is_pic: false,
121            #[cfg(feature = "x86")]
122            x86_features: default_x86_features(Arch::HOST),
123            #[cfg(feature = "aarch64")]
124            aarch64_features: default_aarch64_features(Arch::HOST),
125            #[cfg(feature = "riscv")]
126            riscv_features: default_riscv_features(Arch::HOST),
127        }
128    }
129
130    /// Enable or disable PIC (Position independent code).
131    ///
132    /// If enabled code for calls and jumps to symbols will emit near relocations
133    pub fn set_pic(&mut self, value: bool) {
134        self.is_pic = value;
135    }
136
137    pub fn pic(&self) -> bool {
138        self.is_pic
139    }
140
141    /// Enable or disable an x86 ISA extension.
142    #[cfg(feature = "x86")]
143    pub fn set_x86_feature(&mut self, feature: X86Feature, value: bool) {
144        let feature = feature as usize;
145        let mask = 1u64 << (feature % 64);
146        if value {
147            self.x86_features[feature / 64] |= mask;
148        } else {
149            self.x86_features[feature / 64] &= !mask;
150        }
151    }
152
153    #[cfg(feature = "x86")]
154    pub const fn x86_feature(&self, feature: X86Feature) -> bool {
155        self.x86_feature_id(feature as u8)
156    }
157
158    #[cfg(feature = "x86")]
159    pub(crate) const fn x86_feature_id(&self, feature: u8) -> bool {
160        let feature = feature as usize;
161        feature < CPU_FEATURE_COUNT
162            && (self.x86_features[feature / 64] & (1u64 << (feature % 64))) != 0
163    }
164
165    /// Enables or disables an AArch64 architectural feature.
166    #[cfg(feature = "aarch64")]
167    pub fn set_aarch64_feature(&mut self, feature: AArch64Feature, value: bool) {
168        let mask = 1u64 << feature as usize;
169        if value {
170            self.aarch64_features |= mask;
171        } else {
172            self.aarch64_features &= !mask;
173        }
174    }
175
176    #[cfg(feature = "aarch64")]
177    pub const fn aarch64_feature(&self, feature: AArch64Feature) -> bool {
178        self.aarch64_features & (1u64 << feature as usize) != 0
179    }
180
181    #[cfg(feature = "aarch64")]
182    pub(crate) const fn supports_aarch64_features(&self, required: u64) -> bool {
183        self.aarch64_features & required == required
184    }
185
186    /// Enables or disables a RISC-V extension from the pinned opcode metadata.
187    #[cfg(feature = "riscv")]
188    pub fn set_riscv_feature(&mut self, feature: RiscVFeature, value: bool) {
189        let feature = feature as usize;
190        let mask = 1u64 << (feature % 64);
191        if value {
192            self.riscv_features[feature / 64] |= mask;
193        } else {
194            self.riscv_features[feature / 64] &= !mask;
195        }
196    }
197
198    #[cfg(feature = "riscv")]
199    pub const fn riscv_feature(&self, feature: RiscVFeature) -> bool {
200        let feature = feature as usize;
201        self.riscv_features[feature / 64] & (1u64 << (feature % 64)) != 0
202    }
203
204    #[cfg(feature = "riscv")]
205    pub(crate) const fn supports_any_riscv_feature(
206        &self,
207        required: &[u64; RISCV_FEATURE_WORDS],
208    ) -> bool {
209        let mut index = 0;
210        while index < RISCV_FEATURE_WORDS {
211            if self.riscv_features[index] & required[index] != 0 {
212                return true;
213            }
214            index += 1;
215        }
216        false
217    }
218
219    #[cfg(feature = "x86")]
220    pub fn set_avx(&mut self, value: bool) {
221        self.set_x86_feature(X86Feature::AVX, value);
222    }
223
224    #[cfg(feature = "x86")]
225    pub const fn avx(&self) -> bool {
226        self.x86_feature(X86Feature::AVX)
227    }
228
229    #[cfg(feature = "x86")]
230    pub fn set_avx2(&mut self, value: bool) {
231        self.set_x86_feature(X86Feature::AVX2, value);
232    }
233
234    #[cfg(feature = "x86")]
235    pub const fn avx2(&self) -> bool {
236        self.x86_feature(X86Feature::AVX2)
237    }
238
239    #[cfg(feature = "x86")]
240    pub fn set_avx512f(&mut self, value: bool) {
241        self.set_x86_feature(X86Feature::AVX512_F, value);
242    }
243
244    #[cfg(feature = "x86")]
245    pub const fn avx512f(&self) -> bool {
246        self.x86_feature(X86Feature::AVX512_F)
247    }
248
249    #[cfg(feature = "x86")]
250    pub fn set_avx512bw(&mut self, value: bool) {
251        self.set_x86_feature(X86Feature::AVX512_BW, value);
252    }
253
254    #[cfg(feature = "x86")]
255    pub const fn avx512bw(&self) -> bool {
256        self.x86_feature(X86Feature::AVX512_BW)
257    }
258
259    #[cfg(feature = "x86")]
260    pub fn set_avx512cd(&mut self, value: bool) {
261        self.set_x86_feature(X86Feature::AVX512_CD, value);
262    }
263
264    #[cfg(feature = "x86")]
265    pub const fn avx512cd(&self) -> bool {
266        self.x86_feature(X86Feature::AVX512_CD)
267    }
268
269    #[cfg(feature = "x86")]
270    pub fn set_avx512dq(&mut self, value: bool) {
271        self.set_x86_feature(X86Feature::AVX512_DQ, value);
272    }
273
274    #[cfg(feature = "x86")]
275    pub const fn avx512dq(&self) -> bool {
276        self.x86_feature(X86Feature::AVX512_DQ)
277    }
278
279    #[cfg(feature = "x86")]
280    pub fn set_avx512vl(&mut self, value: bool) {
281        self.set_x86_feature(X86Feature::AVX512_VL, value);
282    }
283
284    #[cfg(feature = "x86")]
285    pub const fn avx512vl(&self) -> bool {
286        self.x86_feature(X86Feature::AVX512_VL)
287    }
288
289    pub const fn arch(&self) -> Arch {
290        self.arch
291    }
292
293    /// Tests whether the environment targets a 32-bit architecture.
294    pub const fn is_32bit(&self) -> bool {
295        self.arch.is_32bit()
296    }
297
298    /// Tests whether the environment targets a 64-bit architecture.
299    pub const fn is_64bit(&self) -> bool {
300        self.arch.is_64bit()
301    }
302}
303
304#[cfg(test)]
305mod tests {
306    use super::Environment;
307    #[cfg(feature = "x86")]
308    use super::X86Feature;
309    #[cfg(feature = "aarch64")]
310    use crate::aarch64::{ALL_CPU_FEATURES as ALL_AARCH64_FEATURES, CPU_FEATURE_REPRESENTATIVE};
311    use crate::core::arch_traits::Arch;
312
313    #[cfg(feature = "x86")]
314    #[test]
315    fn x86_targets_enable_vector_features_by_default() {
316        let env = Environment::new(Arch::X64);
317
318        for feature in crate::x86::instdb::DEFAULT_X86_FEATURES {
319            assert!(env.x86_feature(*feature), "{feature:?}");
320        }
321    }
322
323    #[cfg(feature = "x86")]
324    #[test]
325    fn non_x86_targets_start_without_x86_features() {
326        let env = Environment::new(Arch::AArch64);
327
328        assert!(!env.x86_feature(X86Feature::AVX));
329        assert!(!env.x86_feature(X86Feature::AVX512_F));
330    }
331
332    #[cfg(feature = "x86")]
333    #[test]
334    fn x86_features_can_be_toggled() {
335        let mut env = Environment::new(Arch::X64);
336
337        env.set_avx(false);
338        env.set_x86_feature(X86Feature::AVX512_VL, false);
339
340        assert!(!env.avx());
341        assert!(!env.avx512vl());
342    }
343
344    #[cfg(feature = "x86")]
345    #[test]
346    fn baseline_has_no_optional_x86_features() {
347        let env = Environment::baseline(Arch::X64);
348
349        for feature in crate::x86::instdb::DEFAULT_X86_FEATURES {
350            assert!(!env.x86_feature(*feature), "{feature:?}");
351        }
352    }
353
354    #[cfg(feature = "aarch64")]
355    #[test]
356    fn every_aarch64_feature_is_enabled_by_default_and_removable() {
357        for (feature, instruction) in ALL_AARCH64_FEATURES.iter().zip(CPU_FEATURE_REPRESENTATIVE) {
358            let required = crate::aarch64::INST_FEATURE_MASKS[instruction as usize];
359            let mut environment = Environment::new(Arch::AArch64);
360            assert!(environment.supports_aarch64_features(required));
361            environment.set_aarch64_feature(*feature, false);
362            assert!(
363                !environment.supports_aarch64_features(required),
364                "{feature:?}"
365            );
366        }
367    }
368
369    #[cfg(feature = "riscv")]
370    #[test]
371    fn every_riscv_feature_is_addable_and_removable() {
372        use crate::riscv::instdb::{
373            ALL_CPU_FEATURES, BASELINE_CPU_FEATURES, CPU_FEATURE_REPRESENTATIVE,
374            OPCODE_FEATURE_MASKS,
375        };
376
377        let mut environment = Environment::baseline(Arch::RISCV64);
378        for feature in BASELINE_CPU_FEATURES {
379            environment.set_riscv_feature(*feature, false);
380        }
381        for (feature, opcode) in ALL_CPU_FEATURES.iter().zip(CPU_FEATURE_REPRESENTATIVE) {
382            let required = &OPCODE_FEATURE_MASKS[opcode as usize];
383            environment.set_riscv_feature(*feature, true);
384            assert!(
385                environment.supports_any_riscv_feature(required),
386                "{feature:?}"
387            );
388            environment.set_riscv_feature(*feature, false);
389            assert!(
390                !environment.supports_any_riscv_feature(required),
391                "{feature:?}"
392            );
393        }
394    }
395
396    #[test]
397    fn arch_determines_mode() {
398        assert!(Environment::new(Arch::X86).is_32bit());
399        assert!(!Environment::new(Arch::X86).is_64bit());
400
401        assert!(Environment::new(Arch::X64).is_64bit());
402        assert!(Environment::new(Arch::RISCV32).is_32bit());
403        assert!(Environment::new(Arch::RISCV64).is_64bit());
404        assert!(Environment::new(Arch::AArch64).is_64bit());
405        assert!(!Environment::new(Arch::Unknown).is_32bit());
406        assert!(!Environment::new(Arch::Unknown).is_64bit());
407    }
408}