gruel_cfg/opt/
mod.rs

1//! Optimization level configuration for the Gruel compiler.
2//!
3//! CFG-level optimization passes were removed in ADR-0034. Optimization is now
4//! handled entirely by LLVM's mid-end pipeline (`default<OX>`), which is invoked
5//! when generating object code or LLVM IR at `-O1` and above.
6//!
7//! This module retains the `OptLevel` enum so that the CLI and `CompileOptions`
8//! continue to express the user's requested optimization level.
9
10/// Optimization level, following standard compiler conventions.
11///
12/// Controls the LLVM mid-end optimization pipeline invoked during code
13/// generation. At `-O0` no LLVM passes are run; at `-O1+` the full
14/// `default<OX>` pipeline runs (InstCombine, GVN, SCCP, ADCE, SimplifyCFG, …).
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub enum OptLevel {
17    /// No optimization (`-O0`).
18    ///
19    /// Produces unoptimized code that closely matches the source structure.
20    /// Useful for debugging and faster compilation.
21    #[default]
22    O0,
23
24    /// Basic optimizations (`-O1`).
25    ///
26    /// Runs LLVM's `default<O1>` pipeline.
27    O1,
28
29    /// Standard optimizations (`-O2`).
30    ///
31    /// Runs LLVM's `default<O2>` pipeline.
32    O2,
33
34    /// Aggressive optimizations (`-O3`).
35    ///
36    /// Runs LLVM's `default<O3>` pipeline.
37    O3,
38}
39
40impl OptLevel {
41    /// Returns the name of this optimization level (e.g., "O0", "O1").
42    pub fn name(&self) -> &'static str {
43        match self {
44            OptLevel::O0 => "O0",
45            OptLevel::O1 => "O1",
46            OptLevel::O2 => "O2",
47            OptLevel::O3 => "O3",
48        }
49    }
50
51    /// Returns all available optimization levels.
52    pub fn all() -> &'static [OptLevel] {
53        &[OptLevel::O0, OptLevel::O1, OptLevel::O2, OptLevel::O3]
54    }
55
56    /// Returns a comma-separated string of all level names (for help text).
57    pub fn all_names() -> &'static str {
58        "-O0, -O1, -O2, -O3"
59    }
60}
61
62/// Error returned when parsing an optimization level fails.
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct ParseOptLevelError(String);
65
66impl std::fmt::Display for ParseOptLevelError {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        write!(f, "unknown optimization level '{}'", self.0)
69    }
70}
71
72impl std::error::Error for ParseOptLevelError {}
73
74impl std::str::FromStr for OptLevel {
75    type Err = ParseOptLevelError;
76
77    fn from_str(s: &str) -> Result<Self, Self::Err> {
78        match s {
79            "O0" | "0" => Ok(OptLevel::O0),
80            "O1" | "1" => Ok(OptLevel::O1),
81            "O2" | "2" => Ok(OptLevel::O2),
82            "O3" | "3" => Ok(OptLevel::O3),
83            _ => Err(ParseOptLevelError(s.to_string())),
84        }
85    }
86}
87
88impl std::fmt::Display for OptLevel {
89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        write!(f, "-{}", self.name())
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_opt_level_from_str() {
100        assert_eq!("O0".parse::<OptLevel>().unwrap(), OptLevel::O0);
101        assert_eq!("O1".parse::<OptLevel>().unwrap(), OptLevel::O1);
102        assert_eq!("O2".parse::<OptLevel>().unwrap(), OptLevel::O2);
103        assert_eq!("O3".parse::<OptLevel>().unwrap(), OptLevel::O3);
104
105        // Also accept just the number
106        assert_eq!("0".parse::<OptLevel>().unwrap(), OptLevel::O0);
107        assert_eq!("1".parse::<OptLevel>().unwrap(), OptLevel::O1);
108        assert_eq!("2".parse::<OptLevel>().unwrap(), OptLevel::O2);
109        assert_eq!("3".parse::<OptLevel>().unwrap(), OptLevel::O3);
110
111        // Invalid
112        assert!("O4".parse::<OptLevel>().is_err());
113        assert!("fast".parse::<OptLevel>().is_err());
114    }
115
116    #[test]
117    fn test_opt_level_display() {
118        assert_eq!(format!("{}", OptLevel::O0), "-O0");
119        assert_eq!(format!("{}", OptLevel::O1), "-O1");
120        assert_eq!(format!("{}", OptLevel::O2), "-O2");
121        assert_eq!(format!("{}", OptLevel::O3), "-O3");
122    }
123
124    #[test]
125    fn test_opt_level_default() {
126        assert_eq!(OptLevel::default(), OptLevel::O0);
127    }
128}