Skip to main content

lift_core/
pass.rs

1use crate::context::Context;
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, PartialEq)]
5pub enum PassResult {
6    Unchanged,
7    Changed,
8    RolledBack,
9    Error(String),
10}
11
12impl PassResult {
13    pub fn changed(&self) -> bool {
14        matches!(self, PassResult::Changed)
15    }
16
17    pub fn rolled_back() -> Self {
18        PassResult::RolledBack
19    }
20}
21
22pub trait Pass: std::fmt::Debug {
23    fn name(&self) -> &str;
24    fn run(&self, ctx: &mut Context, cache: &mut AnalysisCache) -> PassResult;
25    fn invalidates(&self) -> Vec<&str> {
26        Vec::new()
27    }
28}
29
30#[derive(Debug, Default)]
31pub struct AnalysisCache {
32    entries: HashMap<String, Box<dyn std::any::Any>>,
33}
34
35impl AnalysisCache {
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    pub fn insert<T: 'static>(&mut self, key: &str, value: T) {
41        self.entries.insert(key.to_string(), Box::new(value));
42    }
43
44    pub fn get<T: 'static>(&self, key: &str) -> Option<&T> {
45        self.entries.get(key)?.downcast_ref::<T>()
46    }
47
48    pub fn invalidate(&mut self, keys: Vec<&str>) {
49        for key in keys {
50            self.entries.remove(key);
51        }
52    }
53
54    pub fn clear(&mut self) {
55        self.entries.clear();
56    }
57}
58
59#[derive(Debug)]
60pub struct PassManager {
61    passes: Vec<Box<dyn Pass>>,
62    cache: AnalysisCache,
63}
64
65impl PassManager {
66    pub fn new() -> Self {
67        Self {
68            passes: Vec::new(),
69            cache: AnalysisCache::new(),
70        }
71    }
72
73    pub fn add_pass(&mut self, pass: Box<dyn Pass>) {
74        self.passes.push(pass);
75    }
76
77    pub fn run_all(&mut self, ctx: &mut Context) -> Vec<(String, PassResult)> {
78        let mut results = Vec::new();
79
80        for pass in &self.passes {
81            let name = pass.name().to_string();
82            let snapshot = ctx.snapshot();
83            let result = pass.run(ctx, &mut self.cache);
84
85            if result.changed() {
86                self.cache.invalidate(pass.invalidates());
87            }
88
89            results.push((name, result));
90            let _ = snapshot;
91        }
92
93        results
94    }
95
96    pub fn num_passes(&self) -> usize {
97        self.passes.len()
98    }
99}
100
101impl Default for PassManager {
102    fn default() -> Self {
103        Self::new()
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[derive(Debug)]
112    struct NoOpPass;
113
114    impl Pass for NoOpPass {
115        fn name(&self) -> &str {
116            "no-op"
117        }
118        fn run(&self, _ctx: &mut Context, _cache: &mut AnalysisCache) -> PassResult {
119            PassResult::Unchanged
120        }
121    }
122
123    #[test]
124    fn test_pass_manager_empty() {
125        let mut pm = PassManager::new();
126        let mut ctx = Context::new();
127        let results = pm.run_all(&mut ctx);
128        assert!(results.is_empty());
129    }
130
131    #[test]
132    fn test_pass_manager_noop() {
133        let mut pm = PassManager::new();
134        pm.add_pass(Box::new(NoOpPass));
135        let mut ctx = Context::new();
136        let results = pm.run_all(&mut ctx);
137        assert_eq!(results.len(), 1);
138        assert_eq!(results[0].1, PassResult::Unchanged);
139    }
140}