Skip to main content

lamina_platform/
cpu_level.rs

1//! x86-64 microarchitecture levels: the instruction-set baseline a generated binary may rely on.
2
3use std::{fmt, str::FromStr};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
6pub enum X86CpuLevel {
7    V1,
8    #[default]
9    V2,
10    V3,
11    V4,
12}
13
14impl X86CpuLevel {
15    pub const ALL: [X86CpuLevel; 4] = [Self::V1, Self::V2, Self::V3, Self::V4];
16
17    pub fn features(self) -> &'static str {
18        match self {
19            Self::V1 => "SSE2",
20            Self::V2 => "SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CMPXCHG16B",
21            Self::V3 => "v2 + AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE",
22            Self::V4 => "v3 + AVX-512F, AVX-512BW, AVX-512CD, AVX-512DQ, AVX-512VL",
23        }
24    }
25}
26
27impl fmt::Display for X86CpuLevel {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        let n = match self {
30            Self::V1 => 1,
31            Self::V2 => 2,
32            Self::V3 => 3,
33            Self::V4 => 4,
34        };
35        write!(f, "x86-64-v{n}")
36    }
37}
38
39impl FromStr for X86CpuLevel {
40    type Err = String;
41
42    fn from_str(s: &str) -> Result<Self, Self::Err> {
43        let lower = s.to_ascii_lowercase();
44        let digit = lower
45            .strip_prefix("x86-64-v")
46            .or_else(|| lower.strip_prefix('v'))
47            .unwrap_or("");
48        match digit {
49            "1" => Ok(Self::V1),
50            "2" => Ok(Self::V2),
51            "3" => Ok(Self::V3),
52            "4" => Ok(Self::V4),
53            _ => Err(format!(
54                "unknown CPU level '{s}' (expected v1, v2, v3 or v4)"
55            )),
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn parses_short_and_psabi_names() {
66        assert_eq!("v1".parse(), Ok(X86CpuLevel::V1));
67        assert_eq!("V2".parse(), Ok(X86CpuLevel::V2));
68        assert_eq!("x86-64-v3".parse(), Ok(X86CpuLevel::V3));
69        assert_eq!("X86-64-V4".parse(), Ok(X86CpuLevel::V4));
70        assert!("v5".parse::<X86CpuLevel>().is_err());
71        assert!("native".parse::<X86CpuLevel>().is_err());
72    }
73
74    #[test]
75    fn levels_are_ordered_and_default_to_v2() {
76        assert!(X86CpuLevel::V1 < X86CpuLevel::V2);
77        assert!(X86CpuLevel::V4 > X86CpuLevel::V3);
78        assert_eq!(X86CpuLevel::default(), X86CpuLevel::V2);
79        assert_eq!(X86CpuLevel::V2.to_string(), "x86-64-v2");
80    }
81}