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}