calyx_opt/
pass_manager.rs1use crate::traversal;
4use calyx_ir as ir;
5use calyx_utils::{CalyxResult, Error};
6use std::collections::{HashMap, HashSet};
7use std::fmt::Write as _;
8use std::time::Instant;
9
10pub type PassClosure = Box<dyn Fn(&mut ir::Context) -> CalyxResult<()>>;
12
13#[derive(Default)]
15pub struct PassManager {
16 passes: HashMap<String, PassClosure>,
18 aliases: HashMap<String, Vec<String>>,
20 help: HashMap<String, String>,
22}
23
24impl PassManager {
25 pub fn register_pass<Pass>(&mut self) -> CalyxResult<()>
34 where
35 Pass:
36 traversal::Visitor + traversal::ConstructVisitor + traversal::Named,
37 {
38 let name = Pass::name().to_string();
39 if self.passes.contains_key(&name) {
40 return Err(Error::misc(format!(
41 "Pass with name '{}' is already registered.",
42 name
43 )));
44 }
45 let pass_closure: PassClosure = Box::new(|ir| {
46 Pass::do_pass_default(ir)?;
47 Ok(())
48 });
49 self.passes.insert(name.clone(), pass_closure);
50 let mut help = format!("- {}: {}", name, Pass::description());
51 for opt in Pass::opts() {
52 write!(
53 &mut help,
54 "\n * {}: {} (default: {})",
55 opt.name(),
56 opt.description(),
57 opt.default()
58 )
59 .unwrap();
60 }
61 self.help.insert(name, help);
62 Ok(())
63 }
64
65 pub fn add_alias(
69 &mut self,
70 name: String,
71 passes: Vec<String>,
72 ) -> CalyxResult<()> {
73 if self.aliases.contains_key(&name) {
74 return Err(Error::misc(format!(
75 "Alias with name '{}' already registered.",
76 name
77 )));
78 }
79 let all_passes = passes
81 .into_iter()
82 .flat_map(|pass| {
83 if self.aliases.contains_key(&pass) {
84 self.aliases[&pass].clone()
85 } else if self.passes.contains_key(&pass) {
86 vec![pass]
87 } else {
88 panic!("No pass or alias named: {}", pass)
89 }
90 })
91 .collect();
92 self.aliases.insert(name, all_passes);
93 Ok(())
94 }
95
96 pub fn specific_help(&self, pass: &str) -> Option<String> {
98 self.help.get(pass).cloned().or_else(|| {
99 self.aliases.get(pass).map(|passes| {
100 let pass_str = passes
101 .iter()
102 .map(|p| format!("- {p}"))
103 .collect::<Vec<String>>()
104 .join("\n");
105 format!("`{pass}' is an alias for pass pipeline:\n{}", pass_str)
106 })
107 })
108 }
109
110 pub fn complete_help(&self) -> String {
113 let mut ret = String::with_capacity(1000);
114
115 let mut pass_names = self.passes.keys().collect::<Vec<_>>();
117 pass_names.sort();
118 ret.push_str("Passes:\n");
119 pass_names.iter().for_each(|&pass| {
120 writeln!(ret, "{}", self.help[pass]).unwrap();
121 });
122
123 let mut aliases = self.aliases.iter().collect::<Vec<_>>();
125 aliases.sort_by(|kv1, kv2| kv1.0.cmp(kv2.0));
126 ret.push_str("\nAliases:\n");
127 aliases.iter().for_each(|(alias, passes)| {
128 let pass_str = passes
129 .iter()
130 .map(|p| p.to_string())
131 .collect::<Vec<String>>()
132 .join(", ");
133 writeln!(ret, "- {}: {}", alias, pass_str).unwrap();
134 });
135 ret
136 }
137
138 fn resolve_alias(&self, maybe_alias: &str) -> Vec<String> {
141 self.aliases
142 .get(maybe_alias)
143 .cloned()
144 .unwrap_or_else(|| vec![maybe_alias.to_string()])
145 }
146
147 fn create_plan(
150 &self,
151 incls: &[String],
152 excls: &[String],
153 ) -> CalyxResult<(Vec<String>, HashSet<String>)> {
154 let passes = incls
156 .iter()
157 .flat_map(|maybe_alias| self.resolve_alias(maybe_alias))
158 .collect::<Vec<_>>();
159
160 let excl_set = excls
161 .iter()
162 .flat_map(|maybe_alias| self.resolve_alias(maybe_alias))
163 .collect::<HashSet<String>>();
164
165 passes.iter().chain(excl_set.iter()).try_for_each(|pass| {
167 if !self.passes.contains_key(pass) {
168 Err(Error::misc(format!(
169 "Unknown pass: {pass}. Run compiler with pass-help subcommand to view registered passes."
170 )))
171 } else {
172 Ok(())
173 }
174 })?;
175
176 Ok((passes, excl_set))
177 }
178
179 pub fn execute_plan(
181 &self,
182 ctx: &mut ir::Context,
183 incl: &[String],
184 excl: &[String],
185 dump_ir: bool,
186 ) -> CalyxResult<()> {
187 let (passes, excl_set) = self.create_plan(incl, excl)?;
188
189 for name in passes {
190 let pass = &self.passes[&name];
193
194 if cfg!(not(target_family = "wasm")) {
197 if !excl_set.contains(&name) {
198 let start = Instant::now();
199 pass(ctx)?;
200 if dump_ir {
201 ir::Printer::write_context(
202 ctx,
203 true,
204 &mut std::io::stdout(),
205 )?;
206 }
207 let elapsed = start.elapsed();
208 if elapsed.as_secs() > 5 {
210 log::warn!("{name}: {}ms", elapsed.as_millis());
211 } else {
212 log::info!("{name}: {}ms", start.elapsed().as_millis());
213 }
214 } else {
215 log::info!("{name}: Ignored")
216 }
217 } else if !excl_set.contains(&name) {
218 pass(ctx)?;
219 }
220 }
221
222 Ok(())
223 }
224}
225
226#[macro_export]
235macro_rules! register_alias {
236 (@unwrap_name $pass:ident) => {
237 $pass::name().to_string()
238 };
239
240 (@unwrap_name $pass:literal) => {
241 $pass.to_string()
242 };
243
244 ($manager:expr, $alias:literal, [ $($pass:tt),* $(,)? ]) => {
245 $manager.add_alias($alias.to_string(), vec![
246 $(register_alias!(@unwrap_name $pass)),*
247 ])?;
248 };
249}