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 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 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 #[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 #[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 #[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 pub const fn is_32bit(&self) -> bool {
295 self.arch.is_32bit()
296 }
297
298 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}